Finding all indexes of a specified character within a string

A simple loop works well:

var str = "scissors";
var indices = [];
for(var i=0; i<str.length;i++) {
    if (str[i] === "s") indices.push(i);
}

Now, you indicate that you want 1,4,5,8. This will give you 0, 3, 4, 7 since indexes are zero-based. So you could add one:

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

and now it will give you your expected result.

A fiddle can be see here.

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

As far as performance goes, I don't think this is something that you need to be gravely worried about until you start hitting problems.

Here is a jsPerf test comparing various answers. In Safari 5.1, the IndexOf performs the best. In Chrome 19, the for loop is the fastest.

enter image description here


Using the native String.prototype.indexOf method to most efficiently find each offset.

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]

This is a micro-optimization, however. For a simple and terse loop that will be fast enough:

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

In fact, a native loop is faster on chrome that using indexOf!

Graph of performance results from the link


benchmark

When i benchmarked everything it seemed like regular expressions performed the best, so i came up with this

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

you can do this

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

which would return

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

i needed a very fast way to match multiple characters against large amounts of text so for example you could do this

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

and you would get this

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

this way you can get all the indexes of your matches in one go


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]

Note that JavaScript counts from 0. Add +1 to i, if you must.