Подтвердить что ты не робот

Как найти индексы всех вхождений одной строки в другую в JavaScript?

Я пытаюсь найти позиции всех вхождений строки в другую строку, не чувствительную к регистру.

Например, с учетом строки:

I learned to play the Ukulele in Lebanon.

и строку поиска le, я хочу получить массив:

[2, 25, 27, 33]

Обе строки будут переменными - т.е. я не могу жестко закодировать их значения.

Я понял, что это была простая задача для регулярных выражений, но, пытаясь найти какое-то время, я бы не повезло.

Я нашел этот пример о том, как это сделать, используя .indexOf(), но, безусловно, должен быть более сжатый способ сделать это?

4b9b3361

Ответ 1

var str = "I learned to play the Ukulele in Lebanon."
var regex = /le/gi, result, indices = [];
while ( (result = regex.exec(str)) ) {
    indices.push(result.index);
}

UPDATE

Я не заметил в исходном вопросе, что строка поиска должна быть переменной. Я написал еще одну версию для рассмотрения этого случая, в которой используется indexOf, поэтому вы вернулись туда, где вы начали. Как отметил Wrikken в комментариях, для этого в общем случае с регулярными выражениями вам нужно будет избежать специальных символов регулярных выражений, после чего я думаю, что решение регулярных выражений становится скорее головной болью, чем это стоит.

function getIndicesOf(searchStr, str, caseSensitive) {
    var searchStrLen = searchStr.length;
    if (searchStrLen == 0) {
        return [];
    }
    var startIndex = 0, index, indices = [];
    if (!caseSensitive) {
        str = str.toLowerCase();
        searchStr = searchStr.toLowerCase();
    }
    while ((index = str.indexOf(searchStr, startIndex)) > -1) {
        indices.push(index);
        startIndex = index + searchStrLen;
    }
    return indices;
}

var indices = getIndicesOf("le", "I learned to play the Ukulele in Lebanon.");

document.getElementById("output").innerHTML = indices + "";
<div id="output"></div>

Ответ 2

Вот регулярное выражение бесплатной версии:

function indexes(source, find) {
  if (!source) {
    return [];
  }
  // if find is empty string return all indexes.
  if (!find) {
    // or shorter arrow function:
    // return source.split('').map((_,i) => i);
    return source.split('').map(function(_, i) { return i; });
  }
  var result = [];
  for (i = 0; i < source.length; ++i) {
    // If you want to search case insensitive use 
    // if (source.substring(i, i + find.length).toLowerCase() == find) {
    if (source.substring(i, i + find.length) == find) {
      result.push(i);
    }
  }
  return result;
}

indexes("I learned to play the Ukulele in Lebanon.", "le")

РЕДАКТИРОВАТЬ: и если вы хотите сопоставить строки, такие как 'aaaa' и 'aa', чтобы найти [0, 2], используйте эту версию:

function indexes(source, find) {
  if (!source) {
    return [];
  }
  if (!find) {
      return source.split('').map(function(_, i) { return i; });
  }
  var result = [];
  var i = 0;
  while(i < source.length) {
    if (source.substring(i, i + find.length) == find) {
      result.push(i);
      i += find.length;
    } else {
      i++;
    }
  }
  return result;
}

Ответ 3

Вы можете это сделать!

//make a regular expression out of your needle
var needle = 'le'
var re = new RegExp(needle,'gi');
var haystack = 'I learned to play the Ukulele';

var results = new Array();//this is the results you want
while (re.exec(haystack)){
  results.push(re.lastIndex);
}

Изменить: научиться писать RegExp

Кроме того, я понял, что это не совсем то, что вы хотите, поскольку lastIndex говорит нам, что конец иглы не является началом, но он близок - вы можете нажать re.lastIndex-needle.length в массив результатов...

Изменить: добавление ссылки

@Tim Down ответ использует объект результатов из RegExp.exec(), и все мои ресурсы Javascript затушевывают его использование (помимо указания строки). Поэтому, когда он использует result.index, это своего рода неназванный объект соответствия. В MDC-описании exec они действительно описывают этот объект в приличной детализации.

Ответ 4

Если вы просто хотите найти позицию всех матчей, я хотел бы указать вам немного взлома:

haystack = 'I learned to play the Ukulele in Lebanon.'
needle = 'le'
splitOnFound = haystack.split(needle).map(function (culm) {
  return this.pos += culm.length + needle.length
}, {pos: -needle.length}).slice(0, -1)

он может быть неприменим, если у вас есть RegExp с переменной длиной, но для некоторых это может быть полезно.

Ответ 5

Используйте String.prototype.match.

Вот пример из самих документов MDN:

var str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
var regexp = /[A-E]/gi;
var matches_array = str.match(regexp);

console.log(matches_array);
// ['A', 'B', 'C', 'D', 'E', 'a', 'b', 'c', 'd', 'e']

Ответ 6

Следуйте ответу @jcubic, его решение вызвало небольшую путаницу для моего случая
Например, var result = indexes('aaaa', 'aa') он вернет [0, 1, 2] вместо [0, 2]
Поэтому я немного обновил его решение, как показано ниже, чтобы соответствовать моему случаю.

function indexes(text, subText, caseSensitive) {
    var _source = text;
    var _find = subText;
    if (caseSensitive != true) {
        _source = _source.toLowerCase();
        _find = _find.toLowerCase();
    }
    var result = [];
    for (var i = 0; i < _source.length;) {
        if (_source.substring(i, i + _find.length) == _find) {
            result.push(i);
            i += _find.length;  // found a subText, skip to next position
        } else {
            i += 1;
        }
    }
    return result;
}

Ответ 7

Вот простой код

function getIndexOfSubStr(str, serchToken, preIndex, output){
		 var result = str.match(serchToken);
     if(result){
     output.push(result.index +preIndex);
     str=str.substring(result.index+serchToken.length);
     getIndexOfSubStr(str, serchToken, preIndex, output)
     }
     return output;
  };

var str = "my name is 'xyz' and my school name is 'xyz' and my area name is 'xyz' ";
var  serchToken ="my";
var preIndex = 0;

console.log(getIndexOfSubStr(str, serchToken, preIndex, []));

Ответ 8

Спасибо за все отклики. Я просмотрел их все и придумал функцию, которая дает первому последний индекс каждого вхождения подстроки 'needle'. Я размещаю это здесь в случае, если это кому-то поможет.

Обратите внимание, что это не то же самое, что первоначальный запрос только для начала каждого вхождения. Он лучше подходит для моего использования, потому что вам не нужно держать длину иглы.

function findRegexIndices(text, needle, caseSensitive){
  var needleLen = needle.length,
    reg = new RegExp(needle, caseSensitive ? 'gi' : 'g'),
    indices = [],
    result;

  while ( (result = reg.exec(text)) ) {
    indices.push([result.index, result.index + needleLen]);
  }
  return indices
}

Ответ 9

Может быть, вам нравится String.split с RegExp, получить массив, а затем пользователь Array.reduce для расчета Indexof

var demoStr = "I learned to play the Ukulele in Lebanon.";
function getStrPositions(sourceStr){
    let indices = [],reg = /(le)/gi,regTest= /le/i;
    sourceStr.split(reg).reduce((prev,curr)=>{
        regTest.test(curr) && indices.push(prev.length)
        return prev+curr
    },'')
    return indices
}

getStrPositions(demoStr)

Ответ 10

приведенный ниже код выполнит вашу работу:

function indexes(source, find) {
  var result = [];
  for(i=0;i<str.length; ++i) {
    // If you want to search case insensitive use 
    // if (source.substring(i, i + find.length).toLowerCase() == find) {
    if (source.substring(i, i + find.length) == find) {
      result.push(i);
    }
  }
  return result;
}

indexes("hello, how are you", "ar")

Ответ 11

function countInString(searchFor,searchIn){

 var results=0;
 var a=searchIn.indexOf(searchFor)

 while(a!=-1){
   searchIn=searchIn.slice(a*1+searchFor.length);
   results++;
   a=searchIn.indexOf(searchFor);
 }

return results;

}