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

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

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

133117 次浏览

一个简单的循环运行良好:

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 表现最好。在 Chrome19中,for 循环是最快的。

enter image description here

使用本机 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更快!

Graph of performance results from the link

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

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

这样你就可以一次性得到所有匹配的索引

更多的功能性乐趣,也更一般: 这会在一个字符串中找到一个长度为 任何的子字符串的起始索引

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]

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


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

您也可以使用 javascript 的 match ()函数。您可以创建一个正则表达式,然后将其作为参数传递给 match ()。

stringName.match(/s/g);

这将返回一个包含所有出现的字母‘ s’的数组。

我喜欢这个问题,并且想用数组上定义的 reduce()方法来写我的答案。

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

为了进一步解决这个问题,我的解决方案是: 你可以在一个字符串中找到字符的索引:

findIndex(str, char) {
const strLength = str.length;
const indexes = [];
let newStr = str;


while (newStr && newStr.indexOf(char) > -1) {
indexes.push(newStr.indexOf(char) + strLength- newStr.length);
newStr = newStr.substring(newStr.indexOf(char) + 1);
}


return indexes;
}


findIndex('scissors', 's'); // [0, 3, 4, 7]
findIndex('Find "s" in this sentence', 's'); // [6, 15, 17]


下面是一个使用函数表达式(带有 ES6箭头函数)的简短解决方案。函数接受字符串和要查找的字符作为参数。它将字符串分割成一个字符数组,并使用 reduce函数将匹配的索引作为数组进行累积并返回。

const findIndices = (str, char) =>
str.split('').reduce((indices, letter, index) => {
letter === char && indices.push(index);
return indices;
}, [])

测试:

findIndices("Hello There!", "e");
// → [1, 8, 10]


findIndices("Looking for new letters!", "o");
// → [1, 2, 9]


下面是一个简洁(一行)的版本:

const findIndices = (str, char) => str.split('').reduce( (indices, letter, index) => { letter === char && indices.push(index); return indices }, [] );

使用 while 循环

let indices = [];
let array = "scissors".split('');
let element = 's';
    

let idx = array.indexOf(element);
    

while (idx !== -1) {
indices.push(idx+1);
idx = array.indexOf(element, idx + 1);
}
console.log(indices);

另一种选择是使用 flatMap

var getIndices = (s, t) => {
return [...s].flatMap((char, i) => (char === t ? i + 1 : []));
};


console.log(getIndices('scissors', 's'));
console.log(getIndices('kaios', '0'));

在现代浏览器中,火柴完成这项工作:

const string = "scissors";
const matches = [...string.matchAll(/s/g)];

你可以通过几种方式得到这些值,例如:

const indexes = matches.map(match => match.index);
function countClaps(str) {
const re = new RegExp(/C/g);


// matching the pattern
const count = str.match(re).length;


return count;
}
//countClaps();


console.log(countClaps("CCClaClClap!Clap!ClClClap!"));