Finding all indexes of a specified character within a string

JavascriptStringIndexing

Javascript Problem Overview


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

Javascript Solutions


Solution 1 - Javascript

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

Solution 2 - Javascript

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

Solution 3 - Javascript

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

Solution 4 - Javascript

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.

Solution 5 - Javascript

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]

Solution 6 - Javascript

In modern browsers matchAll do the job :

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

You can get the values in several ways. For example :

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

Solution 7 - Javascript

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

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

Solution 8 - Javascript

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 }, [] );

Solution 9 - Javascript

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

Solution 10 - Javascript

Another alternative could be using flatMap.

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

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

Solution 11 - Javascript

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

Solution 12 - Javascript

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]

Solution 13 - Javascript

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

Solution 14 - Javascript

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAgeisView Question on Stackoverflow
Solution 1 - JavascriptvcsjonesView Answer on Stackoverflow
Solution 2 - JavascriptPhrogzView Answer on Stackoverflow
Solution 3 - JavascriptChad SciraView Answer on Stackoverflow
Solution 4 - JavascriptTomalakView Answer on Stackoverflow
Solution 5 - JavascriptdavnicwilView Answer on Stackoverflow
Solution 6 - JavascriptJoulssView Answer on Stackoverflow
Solution 7 - JavascripttpdiView Answer on Stackoverflow
Solution 8 - JavascriptRichardView Answer on Stackoverflow
Solution 9 - JavascriptMamunur RashidView Answer on Stackoverflow
Solution 10 - JavascriptPenny LiuView Answer on Stackoverflow
Solution 11 - JavascripthygullView Answer on Stackoverflow
Solution 12 - JavascriptalirezaView Answer on Stackoverflow
Solution 13 - JavascriptS GabaleView Answer on Stackoverflow
Solution 14 - JavascriptYash KalwaniView Answer on Stackoverflow