【问题标题】:Finding all indexes of a specified character within a string查找字符串中指定字符的所有索引
【发布时间】:2012-05-29 10:26:07
【问题描述】:

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

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

【问题讨论】:

  • 您真的不想要基于 1 的字符索引,对吗?
  • 除非你有一个大字符串,或者大量的字符串,或者这种情况经常发生(比如每秒 100 次),否则循环整个字符串可能就足够了。重要的不是效率有多高,而是是不是fast enough
  • 请注意,字符的位置从0(而不是1)开始,这在开始时会令人困惑,但您会通过练习自动完成
  • “我不认为循环遍历整个文件非常有效” - 怎么可能测试字符串中的每个字符没有循环遍历整个字符串?即使有一个内置的.indexOfAll() 方法,它也必须在幕后循环......

标签: javascript string indexing


【解决方案1】:

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

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 循环是最快的。

【讨论】:

  • +1 到目前为止最快的解决方案。 jsperf.com/javascript-string-character-finder
  • 哈哈,我们三个人都进行了自己的 JSPerf 测试;)请注意,循环在 Chrome 上更快,但在 Firefox 和 IE 上更慢(根据我的测试)。
  • @Phrogz 啊,对不起。我的意思是“在 Safari 中,indexOf 是最快的。将它添加到 indexOf 最快的浏览器列表中”
  • @Phrogz 和 vcsjones:你们使用 str[i] 就像它 100% 跨浏览器兼容性...charAt() 更可靠
  • 这就是你应该如何真正测试它,隔离你正在测量的确切内容:jsperf.com/10710345/3
【解决方案2】:

使用原生 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 提到的,如果你(疯狂地)想要基于 1 的值,你可以.push(i+1)
  • +1,但建议在推东西后使用反向?使用unshift()
  • @ajax333221 谢谢你;我还没有测试过unshift() 的速度,但是对于大型数组来说它可能比.push().reverse() 慢。
  • @p true,push + reverse 似乎表现更好in these tests
  • 太棒了。谢谢
【解决方案3】:

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

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

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

【讨论】:

  • 根据我在chrome上跑的benchmark,vcsjones还是最快的jsperf.com/javascript-string-character-finder/6
  • 是的,在一个非常小的字符串上,但是看看当你增加干草堆时会发生什么:jsperf.com/javascript-string-character-finder/7。没有竞争,在我的场景中,我需要在匹配大量文本而不是小字符串方面表现出色的东西。
  • 啊,好吧,公平点 :),也许您应该将该图表添加到您的答案中,以明确为什么您的解决方案实际上是最有效的。
【解决方案4】:
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。

【讨论】:

【解决方案5】:

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

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]

【讨论】:

    【解决方案6】:
    indices = (c, s) => s
              .split('')
              .reduce((a, e, i) => e === c ? a.concat(i) : a, []);
    
    indices('?', 'a?g??'); // [1, 3, 4]
    

    【讨论】:

      【解决方案7】:

      在现代浏览器matchAll 中完成这项工作:

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

      您可以通过多种方式获取值。例如:

      const indexes = matches.map(match => match.index);
      

      【讨论】:

        【解决方案8】:

        这是一个使用函数表达式(带有 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 }, [] );
        

        【讨论】:

          【解决方案9】:

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

          【讨论】:

            【解决方案10】:

            另一种选择是使用flatMap

            var getIndices = (s, t) => {
              return [...s].flatMap((char, i) => (char === t ? i + 1 : []));
            };
            
            console.log(getIndices('scissors', 's'));
            console.log(getIndices('kaios', '0'));

            【讨论】:

              【解决方案11】:

              我喜欢这个问题,并想通过使用数组上定义的 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
              

              【讨论】:

                【解决方案12】:

                只是为了进一步的解决方案,这是我的解决方案: 您可以找到字符串中存在的字符索引:

                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]
                
                

                【讨论】:

                • 虽然此代码可以解决问题,including an explanation 说明如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。请edit您的回答添加解释并说明适用的限制和假设。
                【解决方案13】:

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

                stringName.match(/s/g);
                

                这应该会返回一个包含所有出现的字母“s”的数组。

                【讨论】:

                • 这不会给出索引。
                • @vivex 它会给索引。 match 函数返回附加属性,您可以在其中查看匹配结果的索引。
                猜你喜欢
                • 1970-01-01
                • 2012-11-15
                • 2021-09-09
                • 1970-01-01
                • 2013-11-04
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多