Come trovare l'indice di tutte le occorrenze dell'elemento nell'array?


108

Sto cercando di trovare l'indice di tutte le istanze di un elemento, ad esempio "Nano", in un array JavaScript.

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

Ho provato jQuery.inArray , o in modo simile, .indexOf () , ma ha fornito solo l'indice dell'ultima istanza dell'elemento, cioè 5 in questo caso.

Come lo ottengo per tutte le istanze?

Risposte:


115

Il .indexOf()metodo ha un secondo parametro opzionale che specifica l'indice da cui iniziare la ricerca, quindi puoi chiamarlo in un ciclo per trovare tutte le istanze di un particolare valore:

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

Non è davvero chiaro come si desidera utilizzare gli indici, quindi la mia funzione li restituisce come un array (o restituisce un array vuoto se il valore non viene trovato), ma potresti fare qualcos'altro con i singoli valori dell'indice all'interno del ciclo.

AGGIORNAMENTO: come da commento di VisioN, un semplice ciclo for otterrebbe lo stesso lavoro svolto in modo più efficiente ed è più facile da capire e quindi più facile da mantenere:

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

1
Non sembra essere l'alternativa più veloce a un singolo forciclo con popolamento di array di indici.
VisioN

1
@VisioN - Sì, anche un semplice ciclo for che itera sull'array sarebbe più semplice, ma poiché l'OP ha menzionato il tentativo di usare, .indexOf()volevo dimostrare che può fare il lavoro. (Immagino di aver pensato che l'OP potesse capire come farlo con un ciclo for.) Ovviamente ci sono altri modi per farlo, ad esempio,Cars.reduce(function(a, v, i) { if (v==="Nano") a.push(i); return a; }, []);
nnnnnn

Posso dire che sei del Nord America perché hai usato indexesinvece di indices: P
4castle

2
@ 4castle - Ha. No non sono. "Indici" e "indici" sono entrambi corretti e tendo ad alternarli. Non l'avevo mai considerata una questione di dialetto regionale. Interessante.
nnnnnn

Si noti che il primo esempio fornito funziona alla grande per stringhe e array. Il secondo funziona solo per gli array.
SethWhite

81

Un'altra soluzione alternativa è utilizzare 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: verificare la compatibilità del browser per il reducemetodo e utilizzare polyfill se necessario.


2
+1. Divertente coincidenza: ho appena modificato la mia risposta al tuo commento sotto la mia risposta per suggerire esattamente questa soluzione, quindi aggiorno e vedo che hai già codificato la stessa cosa con un solo nome di variabile diverso.
nnnnnn

@nnnnnn :)Sì, ho pensato che forse reducepotesse essere una bella alternativa.
VisioN

26
array.reduce((a, e, i) => (e === value) ? a.concat(i) : a, [])
yckart

Ho cercato su Google contatè più lento di push, quindi mi attengo alla risposta.
Andre Elrico

54

Un altro approccio che utilizza Array.prototype.map () e Array.prototype.filter () :

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

3
fantastico, funziona. puoi spiegare qual è il ruolo del filtro (String)
Muthamizhchelvan. V

2
@Muthu map(…)controlla ogni iterazione per l'uguaglianza di ee value. Quando corrispondono viene restituito l'indice, altrimenti una stringa vuota. Per sbarazzarsi di quei valori falsi, filter(String)assicurarsi che il risultato contenga solo valori che sono di tipo stringa e NON vuoti. filter(String)potrebbe anche essere scritto come:filter(e => e !== '')
yckart

3
... oppure: String(thing)costringe qualsiasi cosa a una stringa. Array#filterrestituisce un array di tutti i valori per i quali la condizione è veritiera . Poiché le stringhe vuote sono false , NON sono incluse nell'array.
yckart

Grazie per la tua spiegazione, è davvero utile per me
Muthamizhchelvan. V

2
Sarei confuso se lo vedessi in un progetto. Si legge come "Filtra per stringhe", che significa solo mantenere se è una stringa. E poi l'array risultante sarebbe indici come stringhe, non numeri.
Michael Pearson

14

Modo più semplice con lo stile es6.

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

12

Puoi scrivere una semplice soluzione leggibile a questo utilizzando sia mape filter:

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

EDIT: Se non hai bisogno di supportare IE / Edge (o stai traspilando il tuo codice), ES2019 ci ha fornito flatMap , che ti consente di farlo in una semplice riga :

const nanoIndexes = Cars.flatMap((car, i) => car === 'Nano' ? i : []);

6

Nota: MDN fornisce un metodo utilizzando un ciclo while :

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

Non direi che sia migliore di altre risposte. Semplicemente interessante.


4

Voglio solo aggiornare con un altro metodo semplice.

Puoi anche usare forEach metodo.

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

var result = [];

Cars.forEach((car, index) => car === 'Nano' ? result.push(index) : null)

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

1
Gli indici sono a base zero, quindi questo fallirà se la prima macchina è una Nano.
Zac Delventhal

1
Oh guarda, hai una soluzione e la mia sembra proprio così. Avrei dovuto vedere il tuo prima di passare il tempo a scrivere il mio. C'erano così tanti tentacolari per loop che ho pensato: "Potrei fare la mia risposta in 2 secondi".
Michael Pearson

Si. Questi sono per lo più molto complicati. Bella correzione.
Zac Delventhal

2

Questo ha funzionato per 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]

1

Solo per condividere un altro metodo, puoi usare anche i generatori di funzioni per ottenere il risultato:

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


0

Possiamo usare Stack e inserire "i" nello stack ogni volta che incontriamo la condizione "arr [i] == value"

Controllare questo:

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

2
La domanda è contrassegnata con javascript, mentre la tua risposta è Javacredo?
noggin182

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

=> [0, 2]

Scrivi una spiegazione essenziale o commenti in linea per il codice. A proposito, la tua soluzione ha funzionato ma contiene 3 iterazioni ...
JustWe

0

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

}

Usalo in questo modo:

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

result: [0, 3, 7]

-1

findIndexrecupera solo il primo indice che corrisponde all'output di callback. Puoi implementarne uno tuo findIndexesestendendo Array, quindi eseguendo il casting degli array nella nuova struttura.

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


-1

Se intendi usare il carattere di sottolineatura / lodash, potresti farlo

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]

2
Non hai davvero bisogno di alcuna libreria per questo:(["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"]).map((v, i)=> [i, v === "Nano"]).filter(v=>v[1]).map(v=>v[0])
edjroot
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.