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

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

например, учитывая строку:

I learned to play the Ukulele in Lebanon.

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

[2, 25, 27, 33]

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

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

нашел о том, как это сделать с помощью .indexOf(), но, конечно, должен быть более краткий способ сделать это?

9 ответов


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);
}

обновление

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

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>

function indexes(source, find) {
  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")

вы уверены, что можете это сделать!

//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);
}

Edit: научитесь писать RegExp

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

Edit: добавление ссылки

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


использовать строку.прототип.матч.

вот пример из самих документов 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']

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

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)

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


следуйте ответу @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;
}

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

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, []));

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;

}

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

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")