Как найти индексы всех вхождений одной строки в другую в JavaScript? - PullRequest
76 голосов
/ 05 августа 2010

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

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

I learned to play the Ukulele in Lebanon.

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

[2, 25, 27, 33]

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

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

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

Ответы [ 10 ]

130 голосов
/ 05 августа 2010
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, так что вы вернулись к тому, с чего начали.Как указал Вриккен в комментариях, чтобы сделать это для общего случая с регулярными выражениями, вам нужно было бы избегать специальных символов регулярного выражения, и в этот момент я думаю, что решение регулярного выражения становится больше головной болью, чем оно стоит.

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>
14 голосов
/ 05 августа 2010

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

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;
}
11 голосов
/ 05 августа 2010

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

//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 в результаты массив ...

Редактировать: добавить ссылку

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

2 голосов
/ 19 декабря 2017

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

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 с переменной длиной, но длянекоторым это может быть полезно.

0 голосов
/ 11 июня 2019

Спасибо за все ответы.Я просмотрел их все и придумал функцию, которая дает первому последний индекс каждого вхождения подстроки '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
}
0 голосов
/ 02 июля 2018

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

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, []));
0 голосов
/ 19 апреля 2018

Следуйте ответу @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;
}
0 голосов
/ 12 января 2017

Использовать 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']
0 голосов
/ 28 августа 2014

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

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")
0 голосов
/ 28 февраля 2013
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;

}
...