Поиск всех индексов указанного символа в строке

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

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

 mellamokb22 мая 2012 г., 23:27
Если у вас нет большой строки или большого количества строк, или это происходит довольно часто (например, 100 раз в секунду), вероятно, будет достаточно цикла по всей строке. Важно не то, насколько оно эффективно, а то, насколько оно эффективно.fast enough.
 nnnnnn09 авг. 2016 г., 23:58
"I don't think looping through the whole is terribly efficient" - Как можно проверить каждый символ в строкеwithout цикл через всю строку? Даже если там был встроенный.indexOfAll() метод это должно было бы застрять за кулисами ...
 Phrogz22 мая 2012 г., 23:27
Вы действительно не хотите использовать индексы персонажей, основанные на 1?
 ajax33322123 мая 2012 г., 00:36
Обратите внимание, что положение символов начинается с0 (не в1), это поначалу сбивает с толку, но вы будете делать это автоматически с практикой

Ответы на вопрос(8)

Решение Вопроса

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

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

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

Мне понравился вопрос, и я решил написать свой ответ, используя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

Использование родного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

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

indices('?', 'a?g??'); // [1, 3, 4]
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, если вы должны.

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

Более функциональное удовольствие, а также более общее: это находит начальные индексы подстроки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]

 04 авг. 2017 г., 21:04
Плюс один за синтаксис / читабельность

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

stringName.match(/s/g);

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

 28 февр. 2019 г., 11:56
это не даст индексов.

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]}

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

Ваш ответ на вопрос