Вопрос по javascript, string, indexing – Поиск всех индексов указанного символа в строке

44

Например, если бы я имел"scissors" в переменной и хотел знать положение всех вхождений буквы"s"следует распечатать1, 4, 5, 8

Как я могу сделать это в JavaScript наиболее эффективным способом? Я не думаю, что циклически проходить через все это очень эффективно

Если у вас нет большой строки или большого количества строк, или это происходит довольно часто (например, 100 раз в секунду), вероятно, будет достаточно цикла по всей строке. Важно не то, насколько оно эффективно, а то, насколько оно эффективно.fast enough. mellamokb
"I don't think looping through the whole is terribly efficient" - Как можно проверить каждый символ в строкеwithout цикл через всю строку? Даже если там был встроенный.indexOfAll() метод это должно было бы застрять за кулисами ... nnnnnn
Вы действительно не хотите использовать индексы персонажей, основанные на 1? Phrogz
Обратите внимание, что положение символов начинается с0 (не в1), это поначалу сбивает с толку, но вы будете делать это автоматически с практикой ajax333221

Ваш Ответ

8   ответов
70

Простой цикл работает хорошо:

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

Теперь вы указываете, что вы хотите 1,4,5,8. Это даст вам 0, 3, 4, 7, так как индексы начинаются с нуля. Таким образом, вы можете добавить один:

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

и теперь он даст вам ожидаемый результат.

Скрипку можно увидетьВот.

I don't think looping through the whole is terribly efficient

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

ВотJSPerf Тест, сравнивающий различные ответы. В Safari 5.1 IndexOf работает лучше всего. В Chrome 19 цикл for является самым быстрым.

enter image description here

+1 By far самое быстрое решение.jsperf.com/javascript-string-character-finder
Вот как вы должны на самом деле проверить это, выделив именно то, что вы измеряете:jsperf.com/10710345/3
@Phrogz и vcsjones: вы, ребята, использовалиstr[i] как если бы это где 100% кроссбраузерная совместимость ...charAt() гораздо надежнее
LOL, мы все трое сделали наши собственные тесты JSPerf;) Обратите внимание, что циклы быстрее в Chrome, но медленнее в Firefox и IE (согласно моему тесту).
@Phrogz Ах, прости. Я имел в виду & quot; В Safari indexOf самый быстрый. Добавьте его в свой список браузеров, где indexOf - самый быстрый & quot;
0

Мне понравился вопрос, и я решил написать свой ответ, используяreduce() метод, определенный на массивах.

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
20

Использование родногоString.prototype.indexOf способ наиболее эффективно найти каждое смещение.

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]

Это микрооптимизация, однако. Для простого и краткого цикла, который будет достаточно быстрым:

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

In fact, a native loop is faster on chrome that using indexOf!

Graph of performance results from the link

КЛАССНО. Спасибо
Как уже упоминалось @vcsjones, вы можете.push(i+1) если вы (безумно) хотите значения на основе 1.
@ ajax333221 Спасибо за это; Я не проверял скоростьunshift(), но это может быть медленнее для больших массивов, чем.push() а также.reverse().
@p true, push + reverse работает лучшеin these tests
+1, но предлагаете использовать реверс после пуша? использованиеunshift()
1
indices = (c, s) => s
          .split('')
          .reduce((a, e, i) => e === c ? a.concat(i) : a, []);

indices('?', 'a?g??'); // [1, 3, 4]
7
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]

Обратите внимание, что JavaScript считается от 0. Добавить +1 кi, если вы должны.

Чистейший подход, приятно!
@jezternz Наверное, неfastest один, хотя. - На самом деле, это очень медленно.jsperf.com/javascript-string-character-finder
+1 за функциональное веселье, даже если оно неэффективно по сравнению с тем, о чем просил ОП.
5

Более функциональное удовольствие, а также более общее: это находит начальные индексы подстрокиany длина в строке

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]

Плюс один за синтаксис / читабельность
1

Возможно, вы могли бы также использовать функцию match () javascript. Вы можете создать регулярное выражение и затем передать его в качестве параметра в match ().

stringName.match(/s/g);

Это должно вернуть вам массив всех вхождений буквы 's'.

это не даст индексов.
8

benchmark

Когда я сравнил все, казалось, что регулярные выражения показали лучшие результаты, так что я придумал

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

вы можете сделать это

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

который бы вернулся

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

Мне нужен был очень быстрый способ сопоставить несколько символов с большим количеством текста, чтобы, например, вы могли сделать это

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

и ты бы получил это

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

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

Похожие вопросы