2012-05-22 2 views
30

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

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

+3

Вы действительно не хотите индексов символов 1 на основе, не так ли? – Phrogz

+3

Если у вас нет большой строки или большого количества строк, или это случается довольно часто (например, 100 секунд в секунду), то, вероятно, будет достаточно прокручивать всю строку. Важно не то, насколько это эффективно, но это [* достаточно быстро *] (http://stackoverflow.com/a/3770194/116614). – mellamokb

+2

Обратите внимание, что позиция символов начинается с '0' (а не на' 1'), это запутывает в начале, но вы сделаете это автоматически с практикой – ajax333221

ответ

49

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

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

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

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

Я не думаю, что зацикливание через целое очень эффективный

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

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

enter image description here

+2

+1 * На сегодняшний день * самое быстрое решение. http://jsperf.com/javascript-string-character-finder – Tomalak

+3

LOL, мы все трое сделали наши собственные тесты JSPerf;) Обратите внимание, что цикл работает быстрее в Chrome, но медленнее на Firefox и IE (согласно моему тесту). – Phrogz

+0

@Phrogz Великие умы и все. ;) Похоже, Chrome немного выключен. – Tomalak

18

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

На самом деле, родной цикл быстрее на хроме, что с помощью indexOf!

Graph of performance results from the link

+0

Как упоминалось в @vcsjones, вы можете '.push (i + 1)', если вы (безумно) хотите 1-значные значения. – Phrogz

+1

+1, но предлагая использовать реверс после нажатия? используйте 'unshift()' – ajax333221

+0

@ ajax333221 Спасибо за это; Я не проверял скорость 'unshift()', но может быть медленнее для больших массивов, чем '.push()' и '.reverse()'. – Phrogz

6
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, если необходимо.

+3

+1 за функциональную забаву, даже если она злая неэффективна, в отличие от того, что просил ОП. – Phrogz

+0

Самый чистый подход, приятно! –

+0

@jezternz Наверное, не самая быстрая * одна. - На самом деле, это очень медленно. http://jsperf.com/javascript-string-character-finder – Tomalak

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

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

+0

Согласно эталону, я бегал по хроме, vcsjones все еще самый быстрый http://jsperf.com/javascript-string-character-finder/6 – IonicBurger

+0

Да на очень маленькая строка, но посмотрите, что происходит, когда вы увеличиваете стог сена: http://jsperf.com/javascript-string-character-finder/7. Theres нет конкуренции, В моем сценарии мне нужно что-то, что было исполнено в сопоставлении с большими наборами текста, а не крошечной строкой. –

+0

Ah ok fair point :), возможно, вы должны добавить этот график к своему ответу, чтобы дать понять, почему ваше решение на самом деле является наиболее эффективным. – IonicBurger

4

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

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]

+0

Плюс один для синтаксиса/удобочитаемости –

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

indices('?', 'a?g??'); // [1, 3, 4] 
Смежные вопросы