web-dev-qa-db-fra.com

Recherche de tous les index d'un caractère spécifié dans une chaîne

Par exemple, si j'avais "scissors" dans variable et que je voulais connaître la position de toutes les occurrences de la lettre "s", il devrait imprimer 1, 4, 5, 8

Comment puis-je faire cela en JavaScript de la manière la plus efficace? Je ne pense pas que boucler à travers le tout est terriblement efficace

43
Ageis

Une simple boucle fonctionne bien:

var str = "scissors";
var indices = [];
for(var i=0; i<str.length;i++) {
    if (str[i] === "s") indices.Push(i);
}

Maintenant, vous indiquez que vous voulez 1,4,5,8. Cela vous donnera 0, 3, 4, 7 puisque les index sont basés sur zéro. Vous pouvez donc en ajouter un:

if (str[i] === "s") indices.Push(i+1);

et maintenant cela vous donnera le résultat attendu.

Un violon peut être vu ici .

Je ne pense pas que boucler à travers le tout est terriblement efficace

En ce qui concerne les performances, je ne pense pas que ce soit un sujet de préoccupation grave jusqu'à ce que vous commenciez à avoir des problèmes.

Voici un jsPerf test comparant différentes réponses. Dans Safari 5.1, IndexOf est le meilleur. Dans Chrome 19, la boucle for est la plus rapide.

enter image description here

69
vcsjones

Utilisation de la méthode native String.prototype.indexOf pour rechercher le plus efficacement possible chaque décalage.

function locations(substring,string){
  var a=[],i=-1;
  while((i=string.indexOf(substring,i+1)) >= 0) a.Push(i);
  return a;
}

console.log(locations("s","scissors"));
//-> [0, 3, 4, 7]

C'est une micro-optimisation, cependant. Pour une boucle simple et laconique qui sera assez rapide:

// Produces the indices in reverse order; throw on a .reverse() if you want
for (var a=[],i=str.length;i--;) if (str[i]=="s") a.Push(i);    

En fait, une boucle native est plus rapide sur chrome que sur indexOf!

 Graph of performance results from the link

20
Phrogz

 benchmark

Lorsque j’ai tout évalué, il me semblait que les expressions régulières donnaient les meilleurs résultats, alors j’ai eu cette idée.

function indexesOf(string, regex) {
    var match,
        indexes = {};

    regex = new RegExp(regex);

    while (match = regex.exec(string)) {
        if (!indexes[match[0]]) indexes[match[0]] = [];
        indexes[match[0]].Push(match.index);
    }

    return indexes;
}

tu peux le faire

indexesOf('ssssss', /s/g);

qui reviendrait

{s: [0,1,2,3,4,5]}

j'avais besoin d'un moyen très rapide de faire correspondre plusieurs caractères à de grandes quantités de texte. Par exemple, vous pouvez le faire.

indexesOf('dddddssssss', /s|d/g);

et vous obtiendriez ceci

{d:[0,1,2,3,4], s:[5,6,7,8,9,10]}

de cette façon, vous pouvez obtenir tous les index de vos matchs en une seule fois.

8
Chad Scira
function charPos(str, char) {
  return str
         .split("")
         .map(function (c, i) { if (c == char) return i; })
         .filter(function (v) { return v >= 0; });
}

charPos("scissors", "s");  // [0, 3, 4, 7]

Notez que JavaScript compte à partir de 0. Ajoutez +1 à i, si vous devez.

7
Tomalak

Plus amusant, mais aussi plus général: Ceci trouve les index de départ d'une sous-chaîne de any length dans une chaîne

const length = (x) => x.length
const sum = (a, b) => a+b

const indexesOf = (substr) => ({
  in: (str) => (
    str
    .split(substr)
    .slice(0, -1)
    .map(length)
    .map((_, i, lengths) => (
      lengths
      .slice(0, i+1)
      .reduce(sum, i*substr.length)
    ))
  )  
});

console.log(indexesOf('s').in('scissors')); // [0,3,4,7]

console.log(indexesOf('and').in('a and b and c')); // [2,8]

5
davnicwil
indices = (c, s) => s
          .split('')
          .reduce((a, e, i) => e === c ? a.concat(i) : a, []);

indices('?', 'a?g??'); // [1, 3, 4]
1
tpdi

Vous pourriez aussi probablement utiliser la fonction match () de javascript. Vous pouvez créer une expression régulière puis la transmettre en tant que paramètre à match ().

stringName.match(/s/g);

Cela devrait vous retourner un tableau de toutes les occurrences de la lettre.

1
Yash Kalwani

J'ai adoré la question et j'ai pensé écrire ma réponse en utilisant la méthode reduce() définie sur les tableaux.

function getIndices(text, delimiter='.') {
    let indices = [];
    let combined;

    text.split(delimiter)
        .slice(0, -1)
        .reduce((a, b) => { 
            if(a == '') {
                combined = a + b;
            } else { 
                combined = a + delimiter + b;
            } 

            indices.Push(combined.length);
            return combined; // Uncommenting this will lead to syntactical errors
        }, '');

    return indices;
}


let indices = getIndices(`Ab+Cd+Pk+Djb+Nice+One`, '+');
let indices2 = getIndices(`Program.can.be.done.in.2.ways`); // Here default delimiter will be taken as `.`

console.log(indices);  // [ 2, 5, 8, 12, 17 ]
console.log(indices2); // [ 7, 11, 14, 19, 22, 24 ]

// To get output as expected (comma separated)
console.log(`${indices}`);  // 2,5,8,12,17
console.log(`${indices2}`); // 7,11,14,19,22,24
0
hygull