Finding all indexes of a specified character within a string

For example if I had "scissors" in variable and wanted to know the position of all occurrences of the letter "s", it should print out 1, 4, 5, 8

How can I do this in JavaScript in most efficient way? I don't think looping through the whole is terribly efficient

Answers:

Answer

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

Answer

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

Answer

More functional fun, and also more general: This finds the starting indexes of a substring of any length in a string

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]

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

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

I loved the question and thought to write my answer by using the reduce() method defined on arrays.

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
Answer

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

Answer
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.

Answer

You could probably use the match() function of javascript as well. You can create a regular expression and then pass it as a parameter to the match().

stringName.match(/s/g);

This should return you an array of all the occurrence of the the letter 's'.

Answer

Just for further solution, here is my solution: you can find character's indexes which exist in a string:

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]

Answer

Here is a short solution using a function expression (with ES6 arrow functions). The function accepts a string and the character to find as parameters. It splits the string into an array of characters and uses a reduce function to accumulate and return the matching indices as an array.

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

Testing:

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

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

Here is a compact (one-line) version:

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

using while loop

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

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.