144 votes

Comment trouver les indices 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, sans tenir compte de la casse.

Par exemple, étant donné la chaîne de caractères :

I learned to play the Ukulele in Lebanon.

et la chaîne de recherche le Je souhaite obtenir le tableau :

[2, 25, 27, 33]

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

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

J'ai trouvé cet exemple sur la façon d'y parvenir en utilisant .indexOf() Mais il doit bien y avoir un moyen plus concis de le faire.

204voto

Tim Down Points 124501
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);
}

MISE À JOUR

Je n'ai pas remarqué dans la question initiale que la chaîne de recherche doit ê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 l'a souligné Wrikken dans les commentaires, pour faire cela dans le cas général avec des expressions régulières, il faudrait échapper les caractères spéciaux des expressions régulières, et je pense que la solution des expressions régulières devient alors un casse-tête qui n'en vaut pas 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>

46voto

Benny Hinrichs Points 42

One liner utilisant String.prototype.matchAll (ES2020) :

[...sourceStr.matchAll(new RegExp(searchStr, 'gi'))].map(a => a.index)

Utiliser ses valeurs :

const sourceStr = 'I learned to play the Ukulele in Lebanon.';
const searchStr = 'le';
const indexes = [...sourceStr.matchAll(new RegExp(searchStr, 'gi'))].map(a => a.index);
console.log(indexes); // [2, 25, 27, 33]

Si vous êtes inquiet à l'idée de faire un écart et une map() en une seule ligne, je l'ai exécuté avec un for...of pour un million d'itérations (en utilisant vos chaînes). La durée moyenne de la boucle unique est de 1420 ms, tandis que la boucle de for...of 1150ms en moyenne sur ma machine. La différence n'est pas négligeable, mais le one liner fonctionnera très bien si vous ne faites qu'une poignée de matchs.

Véase matchAll sur la caniuse

23voto

jcubic Points 11141

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 Si vous voulez faire correspondre des chaînes de caractères comme '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;
}

22voto

Ryley Points 11916

Vous pouvez certainement le faire !

//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 l'orthographe de RegExp

Aussi, j'ai réalisé que ce n'est pas exactement ce que vous voulez, comme lastIndex nous indique la fin de l'aiguille et non le début, mais c'est proche - vous pourriez pousser re.lastIndex-needle.length dans le tableau des résultats...

Edit : ajout d'un lien

La réponse de @Tim Down utilise l'objet de résultat de RegExp.exec(), et toutes mes ressources Javascript en ignorent l'utilisation (à part le fait de vous donner la chaîne correspondante). Ainsi, lorsqu'il utilise result.index Il s'agit d'une sorte d'objet de correspondance sans nom. Dans le Description de l'exécution par le MDC Ils décrivent en effet cet objet de manière assez détaillée.

6voto

Aditya Kale Points 41

Je suis un peu en retard (de presque 10 ans, 2 mois), mais une façon pour les futurs codeurs est de le faire en utilisant la boucle while et la fonction indexOf()

let haystack = "I learned to play the Ukulele in Lebanon.";
let needle = "le";
let pos = 0; // Position Ref
let result = []; // Final output of all index's.
let hayStackLower = haystack.toLowerCase();

// Loop to check all occurrences 
while (hayStackLower.indexOf(needle, pos) != -1) {
  result.push(hayStackLower.indexOf(needle , pos));
  pos = hayStackLower.indexOf(needle , pos) + 1;
}

console.log("Final ", result); // Returns all indexes or empty array if not found

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X