简体   繁体   中英

How to find the indexes of all occurrences of an element in array?

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, ie 5 in this case.

How do I get it for all instances?

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

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]

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

另一种使用Array.prototype.map()Array.prototype.filter() 的方法

var indices = array.map((e, i) => e === value ? i : '').filter(String)

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

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

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)

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.

const indexes = cars
    .map((car, i) => car === "Nano" ? i : null)
    .filter(i => i !== null)

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]

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

["a", "b", "a", "b"]
   .map((val, index) => ({ val, index }))
   .filter(({val, index}) => val === "a")
   .map(({val, index}) => index)

=> [0, 2]

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]

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

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

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

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

I did not run run all tests我没有运行运行所有测试

If you intend to use underscore/lodash, you could do

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

_.chain(Cars).map((v, i)=> [i, v === "Nano"]).filter(v=>v[1]).map(v=>v[0]).value()

[0, 3, 5]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM