2012-05-22 140 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] 
相关问题