web-dev-qa-db-fra.com

Comment trouver des index de toutes les occurrences d'une chaîne dans une autre en JavaScript?

J'essaie de trouver les positions de toutes les occurrences d'une chaîne dans une autre chaîne, insensible à la casse.

Par exemple, étant donné la chaîne:

le, je veux obtenir le tableau:

[2, 25, 27, 33]

Les deux chaînes seront des variables - c’est-à-dire que je ne peux pas coder en dur leurs valeurs.

Je pensais que c'était une tâche facile pour les expressions régulières, mais après avoir longtemps lutté pour en trouver une qui fonctionnerait, je n'ai pas eu de chance.

J'ai trouvé cet exemple comment accomplir cela en utilisant .indexOf(), mais il doit sûrement y avoir un moyen plus concis de le faire?

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

METTRE À JOUR

J'ai omis de repérer dans la question initiale que la chaîne de recherche devait être une variable. J'ai écrit une autre version pour traiter ce cas qui utilise indexOf, vous êtes donc revenu à votre point de départ. Comme Wrikken l'a souligné dans les commentaires, pour ce faire dans le cas général d'expressions régulières, vous auriez besoin d'échapper à des caractères regex spéciaux. À ce stade, je pense que la solution de regex devient plus un casse-tête qu'elle n'en vaut la peine.

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>

123
Tim Down

Voici la version gratuite de regex:

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

EDIT: et si vous souhaitez faire correspondre des chaînes telles que 'aaaa' et 'aa' pour trouver [0, 2], utilisez cette version:

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;
}
13
jcubic

Vous pouvez vraiment faire ça!

//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: apprendre à épeler RegExp

De plus, j'ai réalisé que ce n'était pas exactement ce que vous voulez, car lastIndex nous indique la fin de l'aiguille, pas le début, mais c'est proche - vous pouvez insérer re.lastIndex-needle.length dans le tableau de résultats ...

Edit: ajout du lien

La réponse de @Tim Down utilise l'objet de résultats de RegExp.exec (), et toutes mes ressources javascript masquent son utilisation (à part vous donner la chaîne correspondante). Ainsi, lorsqu'il utilise result.index, il s'agit d'une sorte d'objet Match non nommé. Dans la description MDC de exec , ils décrivent réellement cet objet avec des détails corrects.

11
Ryley

Si vous voulez juste trouver la position de tous les matchs, j'aimerais vous indiquer un petit hack:

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)

il peut ne pas être applicable si vous avez un RegExp avec une longueur variable, mais pour certains cela peut être utile.

2
Hoffmann

Utilisez String.prototype.match .

Voici un exemple tiré de la documentation 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
tejasbubane

Suivez la réponse de @ jcubic, sa solution a causé une petite confusion pour mon cas
Par exemple, var result = indexes('aaaa', 'aa'), il retournera [0, 1, 2] au lieu de [0, 2] 
J'ai donc mis à jour un peu sa solution comme ci-dessous pour correspondre à mon cas

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
Cao Mạnh Quang

Voici un code simple 

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
Kapil Tiwari

Merci pour toutes vos réponses. Je les ai tous passés en revue et ai mis au point une fonction qui donne à la première un dernier indice de chaque occurrence de la sous-chaîne 'needle'. Je l'affiche ici au cas où cela aiderait quelqu'un.

S'il vous plaît noter que ce n'est pas la même chose que la demande initiale pour seulement le début de chaque occurrence. Cela convient mieux à mon cas d'utilisation car vous n'avez pas besoin de garder la longueur de l'aiguille.

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
Roei Bahumi