¿Cómo encontrar el índice de todas las apariciones del elemento en la matriz?


108

Estoy tratando de encontrar el índice de todas las instancias de un elemento, digamos, "Nano", en una matriz de JavaScript.

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

Probé jQuery.inArray , o de manera similar, .indexOf () , pero sólo di el índice de la última instancia del elemento, es decir, 5 en este caso.

¿Cómo lo obtengo para todas las instancias?

Respuestas:


115

El .indexOf()método tiene un segundo parámetro opcional que especifica el índice desde el que comenzar la búsqueda, por lo que puede llamarlo en un bucle para encontrar todas las instancias de un valor particular:

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

Realmente no deja en claro cómo desea usar los índices, por lo que mi función los devuelve como una matriz (o devuelve una matriz vacía si no se encuentra el valor), pero podría hacer algo más con los valores de índice individuales dentro del bucle.

ACTUALIZACIÓN: según el comentario de VisioN, un bucle for simple haría el mismo trabajo de manera más eficiente, y es más fácil de entender y, por lo tanto, más fácil de mantener:

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

1
No parece ser la alternativa más rápida a un solo forbucle con relleno de matriz de índice.
VisioN

1
@VisioN: Sí, un bucle for simple que itera sobre la matriz también sería más simple, pero como el OP mencionó que intentaba usar .indexOf(), quería mostrar que puede hacer el trabajo. (Supongo que pensé que el OP podría descubrir cómo hacerlo con un bucle for). Por supuesto, hay otras formas de hacerlo, por ejemplo,Cars.reduce(function(a, v, i) { if (v==="Nano") a.push(i); return a; }, []);
nnnnnn

Puedo decir que eres de América del Norte porque indexesindices
usaste en

2
@ 4castle - Ja. No no soy. Los "índices" y los "índices" son correctos y tiendo a alternar entre los dos. Nunca había pensado en eso como una cuestión de dialecto regional. Interesante.
nnnnnn

Tenga en cuenta que el primer ejemplo dado funciona muy bien para cadenas y matrices. El segundo solo funciona para matrices.
SethWhite

80

Otra solución alternativa es utilizar 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: Verifique la compatibilidad del navegador para conocer el reducemétodo y use polyfill si es necesario.


2
+1. Curiosa coincidencia: acabo de editar mi respuesta a su comentario debajo de mi respuesta para sugerir exactamente esta solución, luego actualizo y veo que ya codificó lo mismo con solo un nombre de variable diferente.
nnnnnn

@nnnnnn :)Sí, pensé que tal vez reducepodría ser una buena alternativa.
VisioN

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

Busqué en Google contates más lento que push, por lo tanto, me quedo con la respuesta.
Andre Elrico

54

Otro enfoque que usa Array.prototype.map () y Array.prototype.filter () :

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

3
genial, funciona. ¿Puede explicar cuál es el papel del filtro (String)
Muthamizhchelvan. V

2
@Muthu map(…)comprueba en cada iteración la igualdad de ey value. Cuando coinciden, se devuelve el índice; de ​​lo contrario, una cadena vacía. Para deshacerse de esos valores falsos, filter(String)asegúrese de que el resultado solo contenga valores que sean del tipo de cadena y NO estén vacíos. filter(String)también podría escribirse como:filter(e => e !== '')
yckart

3
... o: String(thing)coacciona cualquier cosa a una cuerda. Array#filterdevuelve una matriz de todos los valores para los que la condición es verdadera . Debido a que las cadenas vacías son falsas , NO se incluyen en la matriz.
yckart

Gracias por su explicación, es realmente útil para mí
Muthamizhchelvan. V

2
Me confundiría si viera esto en un proyecto. Se lee como "Filtrar por cadenas", lo que significa que solo se conserva si es una cadena. Y luego, la matriz resultante serían índices como cadenas, no números.
Michael Pearson

14

Manera más sencilla con estilo 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

Puede escribir una solución legible simple para esto usando mapy filter:

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

EDITAR: Si no necesita ser compatible con IE / Edge (o está transpilando su código), ES2019 nos dio flatMap , que le permite hacer esto en una sola línea:

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

6

Nota: MDN proporciona un método que utiliza un bucle 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);
}

No diría que es mejor que otras respuestas. Solo interesante.


4

Solo quiero actualizar con otro método sencillo.

También puede utilizar el método forEach.

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
Los índices son de base cero, por lo que fallará si el primer automóvil es un Nano.
Zac Delventhal

1
Oh, mira, tienes una solución y la mía parece igual. Debería haber visto el tuyo antes de dedicar tiempo a escribir el mío. Había tantos bucles for extensos que pensé: "Podría hacer mi propia respuesta en 2 segundos".
Michael Pearson

Si. En su mayoría son demasiado complicados. Buena corrección.
Zac Delventhal

2

Esto funcionó para mí:

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 para compartir otro método, puede usar generadores de funciones para lograr el resultado también:

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

Podemos usar Stack e insertar "i" en la pila cada vez que encontremos la condición "arr [i] == value"

Mira esto:

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 pregunta está etiquetada con javascript, mientras que su respuesta es Java¿Creo?
noggin182

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

=> [0, 2]

Escriba una explicación esencial o comentarios en línea para el código. Por cierto, su solución funcionó pero contiene 3 iteraciones ...
JustWe

0

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

}

Úselo así:

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

result: [0, 3, 7]

-1

findIndexrecupera solo el primer índice que coincide con la salida de devolución de llamada. Puede implementar la suya propia findIndexesextendiendo Array y luego convirtiendo sus matrices en la nueva estructura.

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

Si tiene la intención de usar guión bajo / lodash, puede hacer

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
Realmente no necesitas ninguna biblioteca para eso:(["Nano", "Volvo", "BMW", "Nano", "VW", "Nano"]).map((v, i)=> [i, v === "Nano"]).filter(v=>v[1]).map(v=>v[0])
edjroot
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.