179

I am trying to find the indexes of all the instances of an element, say, "Nano", in a JavaScript array.

var Cars = ["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"];

I tried jQuery.inArray, or similarly, .indexOf(), but it only gave the index of the last instance of the element, i.e. 5 in this case.

How do I get it for all instances?

norbdum
  • 2,361
  • 4
  • 19
  • 23

16 Answers16

169

The .indexOf() method has an optional second parameter that specifies the index to start searching from, so you can call it in a loop to find all instances of a particular value:

function getAllIndexes(arr, val) {
    var indexes = [], i = -1;
    while ((i = arr.indexOf(val, i+1)) != -1){
        indexes.push(i);
    }
    return indexes;
}

var indexes = getAllIndexes(Cars, "Nano");

You don't really make it clear how you want to use the indexes, so my function returns them as an array (or returns an empty array if the value isn't found), but you could do something else with the individual index values inside the loop.

UPDATE: As per VisioN's comment, a simple for loop would get the same job done more efficiently, and it is easier to understand and therefore easier to maintain:

function getAllIndexes(arr, val) {
    var indexes = [], i;
    for(i = 0; i < arr.length; i++)
        if (arr[i] === val)
            indexes.push(i);
    return indexes;
}
nnnnnn
  • 147,572
  • 30
  • 200
  • 241
  • 2
    It doesn't seem to be the faster alternative to a single `for` loop with index array populating. – VisioN Dec 27 '13 at 10:04
  • 2
    @VisioN - Yes, a plain for loop iterating over the array would be simpler too, but since the OP mentioned trying to use `.indexOf()` I wanted to show that it can do the job. (I guess I figured the OP could figure out how to do it with a for loop.) Of course there are other ways to do it, e.g., `Cars.reduce(function(a, v, i) { if (v==="Nano") a.push(i); return a; }, []);` – nnnnnn Dec 27 '13 at 10:07
  • I can tell you're from North America because you used `indexes` instead of `indices` :P – 4castle Apr 14 '16 at 21:22
  • 2
    @4castle - Ha. No, I'm not. "Indices" and "indexes" are both correct, and I tend to alternate between the two. I hadn't ever thought of that as a regional dialect thing. Interesting. – nnnnnn Apr 14 '16 at 22:31
  • Note that the first example given works great for strings and arrays. The second only works for arrays. – SethWhite Apr 03 '18 at 19:17
  • I believe `.map()` would be more concise – WebDeg Brian Oct 20 '18 at 22:32
  • @WebDegBrian - `.map()` returns an array the same length as the one it was called on, so you'd also need to use `.filter()` with it. Better to use `.reduce()` as per VisionN's answer. – nnnnnn Nov 18 '18 at 00:45
  • @SethWhite - Sure, but the question was about arrays. (The second one does work to find occurrences of individual characters, but not longer substrings.) – nnnnnn Nov 18 '18 at 00:47
  • It's not good to calculate `arr.length` at each loop, place it before `for` statement or in the first part of `for`,for example `for (let i=0,len=arr.length;i – Igor Fomenko Aug 20 '20 at 08:49
  • 1
    @IgorFomenko - Thanks for the suggestion. It's not really a "calculation", it's just a property lookup, but still actually I do often code loops as per your suggestion, but I don't usually do it in StackOverflow answers if it's not directly relevant to the question. How sure are you that the JS compiler won't do that optimisation automatically behind the scenes? – nnnnnn Aug 20 '20 at 09:43
  • @nnnnnn - I've got this information from some book or w3school or another internet source – Igor Fomenko Aug 20 '20 at 12:16
  • 1
    The `indexOf` recommendation right at the top of the thread in this post is fundamentally inefficient and should be removed. `indexOf` is simply the wrong tool for the job. Use the bottom version or something more idiomatic in one of the answers below this. Regarding "caching" `array.length`--this is a silly micro-optimization that harms readability and introduces potentially subtle and difficult-to-find bugs with virtually no performance benefit. `array.length` is a hashtable lookup, not `strlen` in C that traverses the whole structure item by item. – ggorlen Oct 02 '20 at 17:22
  • @ggorlen - Thanks for the comment. As mentioned in my first comment above, I originally included `indexOf` because the OP mentioned it and so I was just showing a way to make it work without the specific issue OP mentioned. I agree it's not actually a good way to solve the underlying problem, hence the update that doesn't use it, but I'm not going to remove it altogether because I don't like to substantially alter an answer after it was accepted (especially this long after it was accepted). – nnnnnn Oct 03 '20 at 05:10
  • If we want to use the same function on a string (for example, to get the locations of white spaces), will the for loop still be a faster alternative? – batatop Apr 06 '21 at 20:38
131

Another alternative solution is to use Array.prototype.reduce():

["Nano","Volvo","BMW","Nano","VW","Nano"].reduce(function(a, e, i) {
    if (e === 'Nano')
        a.push(i);
    return a;
}, []);   // [0, 3, 5]

N.B.: Check the browser compatibility for reduce method and use polyfill if required.

VisioN
  • 143,310
  • 32
  • 282
  • 281
  • 2
    +1. Funny coincidence: I just edited my reply to your comment under my answer to suggest exactly this solution, then I refresh and see you'd already coded the same thing with only one variable name different. – nnnnnn Dec 27 '13 at 10:15
  • @nnnnnn `:)` Yeah, I thought maybe `reduce` could be a nice alternative. – VisioN Dec 27 '13 at 10:16
  • 35
    `array.reduce((a, e, i) => (e === value) ? a.concat(i) : a, [])` – yckart Dec 21 '16 at 20:46
  • 5
    I googled `contat` is slower than `push`, therefore I stick with the answer. – Andre Elrico Sep 19 '19 at 08:16
  • 2
    Yes, please don't use `concat` here--you're allocating a whole new array object on every callback and tossing the previous object into the garbage collector without any commensurate benefit in readability. – ggorlen Oct 02 '20 at 17:17
  • Then regarding @yckart and @ggorlen: `array.reduce((a, e, i) => (e === value) ? a.push(i) : a, [])` – Darkproduct Dec 11 '20 at 16:09
  • 1
    The @Darkproduct solution will not work. The push method return a number (the length of the array) and not the array itself. So it will raise an error. See : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push – Guix Oct 21 '21 at 19:19
89

Another approach using Array.prototype.map() and Array.prototype.filter():

var indices = array.map((e, i) => e === value ? i : '').filter(String)
yckart
  • 32,460
  • 9
  • 122
  • 129
  • 5
    great, it works. can you explain what is the role of the filter(String) – Muthamizhchelvan. V Jan 03 '18 at 19:40
  • 7
    @Muthu `map(…)` checks on each iteration for the equality of `e` and `value`. When they match the index is returned, otherwise an empty string. To get rid of those falsy values, `filter(String)` makes sure that the result only contains values that are type of string and NOT empty. `filter(String)` could also be written as: `filter(e => e !== '')` – yckart Jan 06 '18 at 19:13
  • 4
    ...or: `String(thing)` coerces anything to a string. `Array#filter` returns an array of all the values for which the condition is [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy). Because empty strings are [falsy](https://developer.mozilla.org/en-US/docs/Glossary/Falsy), those are NOT included in the array. – yckart Jan 06 '18 at 19:13
  • Thank you for your explanation, it's really helpful for me – Muthamizhchelvan. V Jan 07 '18 at 17:32
  • 2
    I would be confused if I saw this in a project. It reads like "Filter to strings", meaning only keep if it's a string. And then the resulting array would be indexes as strings, not numbers. – Michael Pearson Jul 14 '19 at 04:08
  • A real hero here. Thank you +1 – Anjana Silva Aug 27 '20 at 15:56
  • Use `filter(Number)` instead of `filter(String)`. It needs less thinking. – Friedrich -- Слава Україні Oct 23 '22 at 09:47
  • This the way usually this is done is using `Boolean` -> `filter(Boolean)`. `filter(Some callback return true or false)` – pravin Dec 10 '22 at 09:09
  • 1
    Don't use `filter(Number)` @Friedrich--СлаваУкраїні because 0 is equal to false, better to use `filter(String)` because 0 is a type of string. – TopMarx Jul 29 '23 at 15:52
  • @TopMarx 0 is also of type number, hence zero indices are also extracted. – Friedrich -- Слава Україні Jul 29 '23 at 22:28
  • @Friedrich--СлаваУкраїні the example in OP doesn't work using Number, it returns [3,5] whereas using String correctly gives [0,3,5] – TopMarx Jul 31 '23 at 14:34
  • @Friedrich--СлаваУкраїні https://stackoverflow.com/questions/53920037/why-does-array-filternumber-filter-zero-out-in-javascript – TopMarx Jul 31 '23 at 14:48
  • @Friedrich--СлаваУкраїні but you are correct, I was wrong, 0 is a type of number. The reason 0 is filtered out is because it's a falsy value – TopMarx Jul 31 '23 at 14:58
36

More simple way with es6 style.

const indexOfAll = (arr, val) => arr.reduce((acc, el, i) => (el === val ? [...acc, i] : acc), []);


//Examples:
var cars = ["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"];
indexOfAll(cars, "Nano"); //[0, 3, 5]
indexOfAll([1, 2, 3, 1, 2, 3], 1); // [0,3]
indexOfAll([1, 2, 3], 4); // []
awmidas
  • 653
  • 5
  • 13
35

You can write a simple readable solution to this by using both map and filter:

const nanoIndexes = Cars
  .map((car, i) => car === 'Nano' ? i : -1)
  .filter(index => index !== -1);

EDIT: If you don't need to support IE/Edge (or are transpiling your code), ES2019 gave us flatMap, which lets you do this in a simple one-liner:

const nanoIndexes = Cars.flatMap((car, i) => car === 'Nano' ? i : []);
Zac Delventhal
  • 3,543
  • 3
  • 20
  • 26
  • I liked this answer. But how about: changing to this: `.map((car, i) => car === 'Nano' ? i : null).filter(i => i);` – Anton vBR Apr 16 '22 at 19:09
  • 1
    Either works. The problem with using a non-number like null is it will make typing it in TypeScript much more annoying. If you are just using JavaScript, null works fine. – Zac Delventhal Apr 17 '22 at 01:31
12

I just want to update with another easy method.

You can also use forEach method.

var Cars = ["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"];

var result = [];

Cars.forEach((car, index) => car === 'Nano' ? result.push(index) : null)
JKhan
  • 1,157
  • 4
  • 14
  • 23
6

Note: MDN gives a method using a while loop:

var indices = [];
var array = ['a', 'b', 'a', 'c', 'a', 'd'];
var element = 'a';
var idx = array.indexOf(element);
while (idx != -1) {
  indices.push(idx);
  idx = array.indexOf(element, idx + 1);
}

I wouldn't say it's any better than other answers. Just interesting.

abalter
  • 9,663
  • 17
  • 90
  • 145
4
const indexes = cars
    .map((car, i) => car === "Nano" ? i : null)
    .filter(i => i !== null)
Michael Pearson
  • 584
  • 1
  • 4
  • 10
2

This worked for me:

let array1 = [5, 12, 8, 130, 44, 12, 45, 12, 56];
let numToFind = 12
let indexesOf12 = [] // the number whose occurrence in the array we want to find

array1.forEach(function(elem, index, array) {
    if (elem === numToFind) {indexesOf12.push(index)}
    return indexesOf12
})

console.log(indexesOf12) // outputs [1, 5, 7]
Jona Dev
  • 29
  • 5
1

Just to share another method, you can use Function Generators to achieve the result as well:

function findAllIndexOf(target, needle) {
  return [].concat(...(function*(){
    for (var i = 0; i < target.length; i++) if (target[i] === needle) yield [i];
  })());
}

var target = "hellooooo";
var target2 = ['w','o',1,3,'l','o'];

console.log(findAllIndexOf(target, 'o'));
console.log(findAllIndexOf(target2, 'o'));
briosheje
  • 7,356
  • 2
  • 32
  • 54
1
["a", "b", "a", "b"]
   .map((val, index) => ({ val, index }))
   .filter(({val, index}) => val === "a")
   .map(({val, index}) => index)

=> [0, 2]
Dávid Konkoly
  • 1,853
  • 1
  • 13
  • 8
  • Please write an essential explanation or inline comments for the code. BTW, your solution did work but it contains 3 iterations... – JustWe Nov 22 '19 at 00:43
1

You can use Polyfill

if (!Array.prototype.filterIndex) 
{
    Array.prototype.filterIndex = function (func, thisArg) {

        'use strict';
        if (!((typeof func === 'Function' || typeof func === 'function') && this))
            throw new TypeError();

        let len = this.length >>> 0,
            res = new Array(len), // preallocate array
            t = this, c = 0, i = -1;

        let kValue;
        if (thisArg === undefined) {
            while (++i !== len) {
                // checks to see if the key was set
                if (i in this) {
                    kValue = t[i]; // in case t is changed in callback
                    if (func(t[i], i, t)) {
                        res[c++] = i;
                    }
                }
            }
        }
        else {
            while (++i !== len) {
                // checks to see if the key was set
                if (i in this) {
                    kValue = t[i];
                    if (func.call(thisArg, t[i], i, t)) {
                        res[c++] = i;
                    }
                }
            }
        }

        res.length = c; // shrink down array to proper size
        return res;
    };
}

Use it like this:

[2,23,1,2,3,4,52,2].filterIndex(element => element === 2)

result: [0, 3, 7]
EbiPenMan
  • 49
  • 5
1

Also, findIndex() will be useful:

var cars = ['Nano', 'Volvo', 'BMW', 'Nano', 'VW', 'Nano'];

const indexes = [];
const searchedItem = 'NaNo';

cars.findIndex((value, index) => {
  if (value.toLowerCase() === searchedItem.toLowerCase()) {
    indexes.push(index);
  }
});

console.log(indexes); //[ 0, 3, 5 ]

Bonus:

This custom solution using Object.entries() and forEach()

var cars = ['Nano', 'Volvo', 'BMW', 'Nano', 'VW', 'Nano'];

const indexes = [];
const searchableItem = 'Nano';

Object.entries(cars).forEach((item, index) => {
  if (item[1].toLowerCase() === searchableItem.toLowerCase())
    indexes.push(index);
});

console.log(indexes);

Note: I did not run run all tests

0

We can use Stack and push "i" into the stack every time we encounter the condition "arr[i]==value"

Check this:

static void getindex(int arr[], int value)
{
    Stack<Integer>st= new Stack<Integer>();
    int n= arr.length;
    for(int i=n-1; i>=0 ;i--)
    {
        if(arr[i]==value)
        {
            st.push(i);
        }
    }   
    while(!st.isEmpty())
    {
        System.out.println(st.peek()+" ");
        st.pop(); 
    }
}
0

When both parameter passed as array


    function getIndexes(arr, val) {
        var indexes = [], i;
        for(i = 0; i < arr.length; i++){
    for(j =0; j< val.length; j++) {
     if (arr[i] === val[j])
                indexes.push(i);
    }
    }    
        return indexes;
    }

-1

findIndex retrieves only the first index which matches callback output. You can implement your own findIndexes by extending Array , then casting your arrays to the new structure .

class EnhancedArray extends Array {
  findIndexes(where) {
    return this.reduce((a, e, i) => (where(e, i) ? a.concat(i) : a), []);
  }
}
   /*----Working with simple data structure (array of numbers) ---*/

//existing array
let myArray = [1, 3, 5, 5, 4, 5];

//cast it :
myArray = new EnhancedArray(...myArray);

//run
console.log(
   myArray.findIndexes((e) => e===5)
)
/*----Working with Array of complex items structure-*/

let arr = [{name: 'Ahmed'}, {name: 'Rami'}, {name: 'Abdennour'}];

arr= new EnhancedArray(...arr);


console.log(
  arr.findIndexes((o) => o.name.startsWith('A'))
)
Abdennour TOUMI
  • 87,526
  • 38
  • 249
  • 254