Obtenga el índice del objeto dentro de una matriz, que coincida con una condición

322

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.

Vi .indexOf()pero creo que se usa para matrices simples como ["a1","a2",...]. También verifiqué $.grep()pero esto devuelve objetos, no el índice.

amperio
fuente

Respuestas:

733

A partir de 2016, se supone que debe 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 caras, 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');

Al igual que con la mayoría de las optimizaciones, esto debe aplicarse con cuidado y solo cuando sea realmente necesario.

georg
fuente
3
No veo la necesidad de una matriz temporal aquí. Simplemente use el hecho de que la función iteradora se cierra sobre el contexto y use una variable. Además, la versión que no es jQuery no funciona (¿y si se encuentra en el índice 0?). Ambas soluciones hacen más iteraciones de las requeridas, lo cual es menos que ideal si la matriz es grande (aunque las probabilidades de que sea tan grande que un humano lo notaría son bajas, a menos que las búsquedas estén sucediendo mucho ).
TJ Crowder
@ thg435: Todavía creo que es una máquina Rube Goldberg en la que una simple palanca sería suficiente. :-) Pero bueno, ¡funciona!
TJ Crowder
44
¿Puede explicar cómo x => x.prop2=="yutu"funciona con findIndex ()?
Abhay Pai
55
@AbhayPai: es lo mismo quefunction(x) { return x.prop2=="yutu" }
georg
66
Me gusta la sugerencia de usar el polyfill. Sin embargo, el código tal como está escrito todavía falla en IE11 incluso con el polyfill debido al uso de una función de flecha / lambda. Reescrito como index = a.findIndex(function (x) { return x.prop2 == "yutu" })solucionó el problema para que con el código polyfill, findIndex funcionó en IE11
Rick Glos
26

¿Cómo puedo obtener el índice del objeto que coincide con una condición (sin iterar a lo largo de la matriz)?

No puede, algo tiene que iterar a través de la matriz (al menos una vez).

Si la condición cambia mucho, entonces tendrá que recorrer y mirar 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 cuña), esa iteración se puede hacer de manera bastante concisa:

var index;
yourArray.some(function(entry, i) {
    if (entry.prop2 == "yutu") {
        index = i;
        return true;
    }
});

Que utiliza el nuevo (más o menos) Array#somela función , lo que coloca a través de las entradas de la matriz hasta que la función se le da devuelve verdadero. La función que le he dado guarda el índice de la entrada coincidente, luego regresa truepara detener la iteración.

O, por supuesto, solo usa un forbucle. Sus diversas opciones de iteración están cubiertas en esta otra respuesta .

Pero si siempre va a utilizar la misma propiedad para esta búsqueda, y si los valores de las propiedades son únicos, puede recorrer solo una vez y crear un objeto para asignarlos:

var prop2map = {};
yourArray.forEach(function(entry) {
    prop2map[entry.prop2] = entry;
});

(O, de nuevo, puede usar un forbucle o cualquiera de sus otras opciones ).

Luego, si necesita encontrar la entrada con prop2 = "yutu", puede hacer esto:

var entry = prop2map["yutu"];

Yo llamo a esto "indexación cruzada" la matriz. Naturalmente, si elimina o agrega entradas (o cambia sus prop2valores), también necesita actualizar su objeto de mapeo.

TJ Crowder
fuente
¡Gracias por la explicación! La solución con jQuery señaló por thg435hice lo que quería ...
amp
21

Lo que dijo TJ Crowder, de todas formas tendrá algún tipo de iteración oculta, con lodash esto se convierte en:

var index = _.findIndex(array, {prop2: 'yutu'})
aliak
fuente
1
Si bien puede recorrer varias formas de obtener el índice, encontrar Index es la mejor solución, incluso adoptada en ES6 en métodos de matriz nativos
Kelly Milligan,
13
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 los números de matriz básicos también puede 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.

David Castro
fuente
11
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 es verdadero). La asignación individual no es suficiente, debido a un posible índice con 0 (booleano (0) === falso), que no daría lugar a un error pero deshabilita la ruptura 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);
});
Nina Scholz
fuente
¿Qué hace ~ character en tu segundo fragmento?
serkan
@serkan, es un operador NOT bit| a bit , es una versión corta de obtener de un índice (con -1) un resultado verdadero / falso , si existe un índice.
Nina Scholz
gracias Nina, sin el carácter ~, el código funciona como está, ¿no?
serkan
@serkan, su pregunta no está clara, pero sin ~ella no funciona así.
Nina Scholz
1
Ah, !!(index = 0)y la !!~(index = 0)diferencia de hecho. ¡Gracias!
serkan
4

Puede usar Array.prototype.some () de la siguiente manera (como se menciona en las otras respuestas):

https://jsfiddle.net/h1d69exj/2/

function findIndexInData(data, property, value) {
    var result = -1;
    data.some(function (item, i) {
        if (item[property] === value) {
            result = i;
            return true;
        }
    });
    return result;
}
var data = [{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"}]



alert(findIndexInData(data, 'prop2', "yutu")); // shows index of 1
GibboK
fuente
4

He visto muchas soluciones en lo anterior.

Aquí estoy usando la función de mapa para encontrar el índice del texto de búsqueda en un objeto de matriz.

Voy a explicar mi respuesta usando los datos de los estudiantes.

  • Paso 1 : crea un objeto de matriz para los estudiantes (opcionalmente puedes crear tu propio objeto de matriz).
    var students = [{name:"Rambabu",htno:"1245"},{name:"Divya",htno:"1246"},{name:"poojitha",htno:"1247"},{name:"magitha",htno:"1248"}];

  • Paso 2 : crea una variable para buscar texto
    var studentNameToSearch = "Divya";

  • Paso 3 : Crear una variable para almacenar el índice coincidente (aquí usamos la función de mapa para iterar).
    var matchedIndex = students.map(function (obj) { return obj.name; }).indexOf(studentNameToSearch);

var students = [{name:"Rambabu",htno:"1245"},{name:"Divya",htno:"1246"},{name:"poojitha",htno:"1247"},{name:"magitha",htno:"1248"}];

var studentNameToSearch = "Divya";

var matchedIndex = students.map(function (obj) { return obj.name; }).indexOf(studentNameToSearch);

console.log(matchedIndex);

alert("Your search name index in array is:"+matchedIndex)

Rambabu Bommisetti
fuente
3
function findIndexByKeyValue(_array, key, value) {
    for (var i = 0; i < _array.length; i++) { 
        if (_array[i][key] == value) {
            return i;
        }
    }
    return -1;
}
var a = [
    {prop1:"abc",prop2:"qwe"},
    {prop1:"bnmb",prop2:"yutu"},
    {prop1:"zxvz",prop2:"qwrq"}];
var index = findIndexByKeyValue(a, 'prop2', 'yutu');
console.log(index);
Pranabesh Chand
fuente
1

¿Por qué no quieres iterar exactamente? ¡Los nuevos Array.prototype.forEach son excelentes para este propósito!

Si lo desea, puede usar un Árbol de búsqueda binaria para buscar a través de una llamada a un solo método. Esta es una implementación ordenada del árbol de búsqueda BTree y Red black Search en JS: https://github.com/vadimg/js_bintrees , pero no estoy seguro de si puede encontrar el índice al mismo tiempo.

Rishabh
fuente
1

Un paso usando Array.reduce () - no jQuery

var items = [{id: 331}, {id: 220}, {id: 872}];

var searchIndexForId = 220;
var index = items.reduce(function(searchIndex, item, index){
  if(item.id === searchIndexForId) { 
    console.log('found!');
    searchIndex = index;
  }
  return searchIndex;
}, null);

regresará nullsi no se encontró el índice.

SagiSergeNadir
fuente
0
var list =  [
                {prop1:"abc",prop2:"qwe"},
                {prop1:"bnmb",prop2:"yutu"},
                {prop1:"zxvz",prop2:"qwrq"}
            ];

var findProp = p => {
    var index = -1;
    $.each(list, (i, o) => {
        if(o.prop2 == p) {
            index = i;
            return false; // break
        }
    });
    return index; // -1 == not found, else == index
}
Ruben Morales Felix
fuente
0

Georg ya ha mencionado que ES6 tiene Array.findIndex para esto. Y algunas otras respuestas son soluciones para ES5 usando el método Array.some.

Un enfoque más elegante puede ser

var index;
for(index = yourArray.length; index-- > 0 && yourArray[index].prop2 !== "yutu";);

Al mismo tiempo, me gustaría enfatizar que Array.some puede implementarse con una técnica de búsqueda eficiente binaria u otra. Por lo tanto, podría funcionar mejor para loop en algunos navegadores.

Sanjoy
fuente
0

Prueba este código

var x = [{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"}]
let index = x.findIndex(x => x.prop1 === 'zxvz')
Trilok Singh
fuente