查找字符串中指定字符的所有索引

IT技术 javascript string indexing
2021-01-18 20:06:11

例如,如果我有"scissors"in 变量并想知道所有出现的字母的位置"s",它应该打印出1, 4, 5, 8.

我怎样才能以最有效的方式在 JavaScript 中做到这一点?我不认为遍历整个过程非常有效

6个回答

一个简单的循环效果很好:

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

现在它会给你你预期的结果。

一个小提琴可以在这里看到

我不认为遍历整个过程非常有效

就性能而言,我不认为这是您在开始遇到问题之前需要严重担心的事情。

这是一个比较各种答案jsPerf测试。在 Safari 5.1 中,IndexOf 表现最好。在 Chrome 19 中,for 循环是最快的。

在此处输入图片说明

@Phrogz 啊,对不起。我的意思是“在 Safari 中,indexOf 是最快的。将它添加到 indexOf 最快的浏览器列表中”
2021-03-13 20:06:11
大声笑,我们三个都做了我们自己的 JSPerf 测试 ;) 请注意,循环在 Chrome 上更快,但在 Firefox 和 IE 上更慢(根据我的测试)。
2021-03-31 20:06:11
+1迄今为止最快的解决方案。jsperf.com/javascript-string-character-finder
2021-04-03 20:06:11
这是你应该如何真正测试它,隔离你正在测量的确切东西:jsperf.com/10710345/3
2021-04-04 20:06:11
@Phrogz 和 vcsjones:你们使用了str[i]100% 跨浏览器兼容性......charAt()更可靠
2021-04-11 20:06:11

使用本机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);    

事实上,原生循环在 chrome 上比使用indexOf!

来自链接的性能结果图

正如@vcsjones 所提到的,.push(i+1)如果您(疯狂地)想要基于 1 的值,则可以。
2021-03-17 20:06:11
@p true,push + reverse 似乎在这些测试中表现更好
2021-03-18 20:06:11
+1,但建议在推送东西后使用反向?利用unshift()
2021-03-20 20:06:11
惊人的。谢谢
2021-03-23 20:06:11
@ajax333221 谢谢你;我还没有测试 的速度unshift(),但对于大型数组,它可能比 a.push().reverse()
2021-04-08 20:06:11

基准

当我对所有内容进行基准测试时,正则表达式似乎表现最好,所以我想出了这个

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

通过这种方式,您可以一次性获得所有匹配项的索引

在一个非常小的字符串上是的,但是看看当你增加 haystack 时会发生什么:jsperf.com/javascript-string-character-finder/7没有竞争,在我的场景中,我需要一些在匹配大量文本而不是小字符串方面表现出色的东西。
2021-03-13 20:06:11
好吧,公平点:),也许您应该将该图添加到您的答案中,以明确为什么您的解决方案实际上是最有效的。
2021-03-22 20:06:11
根据我在 chrome 上运行的基准,vcsjones 仍然是最快的jsperf.com/javascript-string-character-finder/6
2021-03-24 20:06:11
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 开始计数i。如果必须,请将+1 添加到

+1 用于功能性乐趣,即使与 OP 要求的相比,它的效率非常低。
2021-04-03 20:06:11
不过,@jezternz 可能不是最快的——实际上,它很慢。jsperf.com/javascript-string-character-finder
2021-04-03 20:06:11

功能更有趣,也更通用:这将查找字符串中任意长度的子字符串的起始索引

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]