关于javascript:查找字符串中指定字符的所有索引

Finding all indexes of a specified character within a string

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

如何以最有效的方式在JavaScript中实现这一点?我不认为循环整个过程是非常有效的


一个简单的循环很好地工作:

1
2
3
4
5
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,因为索引是从零开始的。所以你可以添加一个:

1
if (str[i] ==="s") indices.push(i+1);

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

这里可以看到一把小提琴。

I don't think looping through the whole is terribly efficient

就表现而言,我不认为这是你需要认真担心的事情,直到你开始遇到问题。

这里是一个比较各种答案的JSPERF测试。在Safari 5.1中,indexof的性能最好。在Chrome19中,for循环是最快的。

enter image description here


使用本机String.prototype.indexOf方法最有效地找到每个偏移量。

1
2
3
4
5
6
7
8
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]

然而,这是一个微观优化。对于速度足够快的简单而简洁的循环:

1
2
// 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更快!

氧化镁


氧化镁

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

1
2
3
4
5
6
7
8
9
10
11
12
13
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;
}

你可以这样做

1
indexesOf('ssssss', /s/g);

哪一个会回来

1
{s: [0,1,2,3,4,5]}

我需要一种非常快速的方法来匹配大量文本中的多个字符,例如,您可以这样做

1
indexesOf('dddddssssss', /s|d/g);

你会得到这个

1
{d:[0,1,2,3,4], s:[5,6,7,8,9,10]}

这样,您就可以一次性获取所有匹配项的索引。


1
2
3
4
5
6
7
8
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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]


1
2
3
4
5
indices = (c, s) => s
          .split('')
          .reduce((a, e, i) => e === c ? a.concat(i) : a, []);

indices('?', 'a?g??'); // [1, 3, 4]


你也可以使用JavaScript的Matd()函数。您可以创建正则表达式,然后将其作为参数传递给子表达式。

1
stringName.match(/s/g);

这将返回一个字母S的所有出现的数组。


我喜欢这个问题,并想通过使用在数组上定义的EDCOX1×3方法来写我的答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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