Obtener el índice del objeto dentro de una matriz, cumpliendo una condición
Tengo una matriz como esta:
[{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"},...]
¿Cómo puedo obtener el índice del objeto que coincide con una condición, sin iterar sobre toda la matriz?
Por ejemplo, dado prop2=="yutu"
, quiero obtener el índice 1
.
Lo vi, .indexOf()
pero creo que se usa para matrices simples como ["a1","a2",...]
. También lo verifiqué $.grep()
pero esto devuelve objetos, no el índice.
A partir de 2016, se supone que debes usar Array.findIndex
(un estándar ES2015/ES6) para esto:
a = [
{prop1:"abc",prop2:"qwe"},
{prop1:"bnmb",prop2:"yutu"},
{prop1:"zxvz",prop2:"qwrq"}];
index = a.findIndex(x => x.prop2 ==="yutu");
console.log(index);
Es compatible con Google Chrome, Firefox y Edge. Para Internet Explorer, hay un polyfill en la página vinculada.
Nota de rendimiento
Las llamadas a funciones son costosas, por lo tanto, con matrices realmente grandes, un bucle simple funcionará mucho mejor que findIndex
:
let test = [];
for (let i = 0; i < 1e6; i++)
test.push({prop: i});
let search = test.length - 1;
let count = 100;
console.time('findIndex/predefined function');
let fn = obj => obj.prop === search;
for (let i = 0; i < count; i++)
test.findIndex(fn);
console.timeEnd('findIndex/predefined function');
console.time('findIndex/dynamic function');
for (let i = 0; i < count; i++)
test.findIndex(obj => obj.prop === search);
console.timeEnd('findIndex/dynamic function');
console.time('loop');
for (let i = 0; i < count; i++) {
for (let index = 0; index < test.length; index++) {
if (test[index].prop === search) {
break;
}
}
}
console.timeEnd('loop');
Como ocurre con la mayoría de las optimizaciones, esto debe aplicarse con cuidado y sólo cuando sea realmente necesario.
¿Cómo puedo obtener el índice del objeto que coincide con una condición (sin iterar a lo largo de la matriz)?
No se puede, algo tiene que recorrer la matriz (al menos una vez).
Si la condición cambia mucho, tendrás que recorrer y observar los objetos que contiene para ver si coinciden con la condición. Sin embargo, en un sistema con características ES5 (o si instala una corrección), esa iteración se puede realizar de manera bastante concisa:
var index;
yourArray.some(function(entry, i) {
if (entry.prop2 == "yutu") {
index = i;
return true;
}
});
Eso utiliza la Array#some
función new(ish) , que recorre las entradas de la matriz hasta que la función que le asigna devuelve verdadero. La función que le he asignado guarda el índice de la entrada coincidente y luego regresa true
para detener la iteración.
O, por supuesto, simplemente use un for
bucle. Sus diversas opciones de iteración se tratan en esta otra respuesta .
Pero si siempre va a utilizar la misma propiedad para esta búsqueda y si los valores de la propiedad son únicos, puede realizar un bucle solo una vez y crear un objeto para asignarlos:
var prop2map = {};
yourArray.forEach(function(entry) {
prop2map[entry.prop2] = entry;
});
(O, nuevamente, podrías usar un for
bucle o cualquiera de tus otras opciones ).
Luego, si necesita encontrar la entrada con prop2 = "yutu"
, puede hacer esto:
var entry = prop2map["yutu"];
A esto lo llamo "indexación cruzada" de la matriz. Naturalmente, si elimina o agrega entradas (o cambia sus prop2
valores), también deberá actualizar su objeto de mapeo.
Lo que dijo TJ Crowder, siempre tendrá algún tipo de iteración oculta, con lodash esto se convierte en:
var index = _.findIndex(array, {prop2: 'yutu'})
var CarId = 23;
//x.VehicleId property to match in the object array
var carIndex = CarsList.map(function (x) { return x.VehicleId; }).indexOf(CarId);
Y para números de matriz básicos también puedes hacer esto:
var numberList = [100,200,300,400,500];
var index = numberList.indexOf(200); // 1
Obtendrá -1 si no puede encontrar un valor en la matriz.
var index;
yourArray.some(function (elem, i) {
return elem.prop2 === 'yutu' ? (index = i, true) : false;
});
Iterar sobre todos los elementos de la matriz. Devuelve el índice y verdadero o falso si la condición no coincide.
Importante es el valor de retorno explícito de verdadero (o un valor cuyo resultado booleano sea verdadero). La asignación única no es suficiente, debido a un posible índice con 0 (Boolean(0) === false), que no resultaría en un error pero deshabilitaría la interrupción de la iteración.
Editar
Una versión aún más corta de lo anterior:
yourArray.some(function (elem, i) {
return elem.prop2 === 'yutu' && ~(index = i);
});