2012-05-22 183 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

大聲笑,我們三人都做了我們自己的JSPerf測試;)請注意,循環在Chrome上更快,但在Firefox和IE上更慢(根據我的測試)。 – Phrogz

+0

@Progrog偉大的思想和所有。 ;)看起來像鉻是有點關閉。 – 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所提到的,如果您(瘋狂地)需要基於1的值,您可以'.push(i + 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的功能樂趣,即使它的惡作劇與OP所要求的相反,效率很低。 – 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

根據我在chrome上運行的基準,vcsjones仍然是最快的http://jsperf.com/javascript-string-character-finder/6 – IonicBurger

+0

是的一個非常小的字符串,但看看當你增加乾草堆時會發生什麼:http://jsperf.com/javascript-string-character-finder/7。 Theres沒有競爭,在我的情況下,我需要一些能夠匹配大量文本而不是小字符串的高性能應用程序。 –

+0

好的公平點:),也許你應該將圖表添加到你的答案中,以明確爲什麼你的解決方案實際上是最有效的。 – 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] 
相關問題