Método indexOf en una matriz de objetos?

514

¿Cuál es el mejor método para obtener el índice de una matriz que contiene objetos?

Imagina este escenario:

var hello = {
    hello: 'world',
    foo: 'bar'
};
var qaz = {
    hello: 'stevie',
    foo: 'baz'
}

var myArray = [];
myArray.push(hello,qaz);

Ahora me gustaría tener el indexOfobjeto cuya hellopropiedad es 'stevie'cuál, en este ejemplo, sería 1.

Soy bastante novato con JavaScript y no sé si hay un método simple o si debería crear mi propia función para hacerlo.

Antonio Laguna
fuente
1
¿Quieres fusionar los dos objetos helloy qaz?
Armin
No, no lo hago. Quiero tener una lista de objetos en una matriz.
Antonio Laguna
¡Ah bien! Desea conocer la posición de todo el objeto en la matriz, que tiene una propiedad definida.
Armin
10
Encontré una función muy simple para resolver este problema exacto con esta respuesta SO: var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor); var objectFound = array[elementPos]; [link] ( stackoverflow.com/a/16100446/1937255 )
Rick
ES6 Array.indexOf es mejor que la respuesta aceptada (si ES6 funciona para usted) - vea el ejemplo completo a continuación
yar1

Respuestas:

1045

Creo que puedes resolverlo en una línea usando la función de mapa :

pos = myArray.map(function(e) { return e.hello; }).indexOf('stevie');
Pablo Francisco Pérez Hidalgo
fuente
58
Honestamente, esta debería ser la respuesta aceptada. La mayoría de los navegadores de hoy en día son compatiblesArray.prototype.map()
AlbertEngelB
10
No es compatible con IE8 pero, si eso no es un problema, esta es la mejor solución.
Antonio Laguna
57
Um ... ¿no vale la pena señalar que Array.prototype.map () crea una matriz completamente nueva que contiene los elementos asignados? Entonces, si tienes una matriz con 1000 elementos, primero has creado otra matriz con 1000 elementos, ¿luego buscas? Merecería la pena ver el rendimiento de este método frente a un bucle simple. Especialmente cuando se ejecuta en una plataforma móvil con recursos limitados.
Doug
77
@Doug Aunque su punto sobre el rendimiento es correcto, ¿quién en su sano juicio reemplazaría una línea de código con siete para una aplicación que está casi por definición vinculada a IO / Red hasta que se hayan detectado cuellos de botella?
Jared Smith el
66
Técnicamente, un archivo js minimizado también es una línea; D
Greater King
371

Array.prototype.findIndex es compatible con todos los navegadores que no sean IE (no edge). Pero el polyfill proporcionado es agradable.

var indexOfStevie = myArray.findIndex(i => i.hello === "stevie");

La solución con el mapa está bien. Pero está iterando sobre toda la matriz en cada búsqueda. Ese es solo el peor de los casos para findIndex, que deja de iterar una vez que se encuentra una coincidencia.


Realmente no hay una manera concisa (cuando los desarrolladores tenían que preocuparse por IE8) , pero aquí hay una solución común:

var searchTerm = "stevie",
    index = -1;
for(var i = 0, len = myArray.length; i < len; i++) {
    if (myArray[i].hello === searchTerm) {
        index = i;
        break;
    }
}

o como una función:

function arrayObjectIndexOf(myArray, searchTerm, property) {
    for(var i = 0, len = myArray.length; i < len; i++) {
        if (myArray[i][property] === searchTerm) return i;
    }
    return -1;
}
arrayObjectIndexOf(arr, "stevie", "hello"); // 1

Solo algunas notas:

  1. No lo use para ... en bucles en matrices
  2. Asegúrese de salir del bucle o volver a salir de la función una vez que haya encontrado su "aguja"
  3. Tenga cuidado con la igualdad de objetos.

Por ejemplo,

var a = {obj: 0};
var b = [a];
b.indexOf({obj: 0}); // -1 not found
Joe
fuente
la función tiene la comparación de términos de búsqueda incorrecta como debería ser searchTerm :)
Antonio Laguna
hubo múltiples ocurrencias
Joe
3
@SteveBennett es una versión de rendimiento optimizado; la longitud de la matriz debe determinarse solo una vez (cuando se inicializan las variables para el bucle for). En su caso, la longitud se verifica cada iteración de nuevo. Consulte también stackoverflow.com/questions/5349425/… y stackoverflow.com/questions/8452317/… Sin embargo, si el rendimiento no es alto, realmente no importa.
loother
1
Buena respuesta, pero hice algunos benchmarking de rendimiento (ver jsperf.com/find-index-of-object-in-array-by-contents ), y descubrí que la respuesta basada en funciones mencionada aquí parece ser la segunda respuesta más eficaz. Lo único más eficaz es ponerlo en un prototipo, en lugar de solo una función, como se menciona en mi respuesta .
Uniphonic
123

En ES2015, esto es bastante fácil:

myArray.map(x => x.hello).indexOf('stevie')

o, probablemente con un mejor rendimiento para matrices más grandes:

myArray.findIndex(x => x.hello === 'stevie')
Steve Bennett
fuente
2
Buen enfoque para usar ES6
kag
Me sorprendió que ninguno de estos métodos sea tan eficaz como el prototipo de bucle, como se menciona en mi respuesta. A pesar de que el soporte del navegador del método findIndex es un poco pobre, parece que estaría haciendo lo mismo, pero aún así es menos eficiente. Vea el enlace en mi respuesta para los puntos de referencia.
Uniphonic
Es bueno saber si el rendimiento es importante para la tarea en cuestión. Muy raramente es, en mi experiencia, pero mmm.
Steve Bennett
24
var idx = myArray.reduce( function( cur, val, index ){

    if( val.hello === "stevie" && cur === -1 ) {
        return index;
    }
    return cur;

}, -1 );
Esailija
fuente
17

Me gusta la respuesta de Pablo, pero Array # indexOf y Array # map no funcionan en todos los navegadores. El subrayado utilizará código nativo si está disponible, pero también tiene retrocesos. Además, tiene el método de extracción para hacer exactamente lo que hace el método de mapa anónimo de Pablo.

var idx = _.chain(myArray).pluck("hello").indexOf("Stevie").value();
Tandrewnichols
fuente
1
Array.prototype.map () está soportado para IE9 + y puede usar un Polyfill para IE8, 7, 6: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Johann Echavarria
1
Podrías usar un polyfill. . . o simplemente puede usar subrayado o lodash, que son básicamente polyfills que tienen un montón de otras cosas adjuntas . ¿Cuál es la objeción con el guión bajo? ¿Talla?
tandrewnichols 01 de
Realmente me gusta Underscore, tu respuesta también es inteligente, pero en mi humilde opinión, la respuesta de Pablo es la más clara.
Johann Echavarria
Wow, nunca pensé usar el encadenamiento así. Realmente me gusta cómo hace que la búsqueda sea fluida.
Dylan Pierce
chainEs superfluo aquí. _.pluck(myArray, 'hello').indexOf('stevie')
Steve Bennett
14

O prototipo:

Array.prototype.indexOfObject = function arrayObjectIndexOf(property, value) {
    for (var i = 0, len = this.length; i < len; i++) {
        if (this[i][property] === value) return i;
    }
    return -1;
}

myArr.indexOfObject("name", "stevie");
Nathan Zaetta
fuente
99
¡Muy conveniente! Aunque elegiría prototype.indexOfObject para no interferir con el método existente Array.indexOf. Array.prototype.indexOfObject = function(property, value) { for (var i = 0, len = this.length; i < len; i++) { if (this[i][property] === value) return i; } return -1; };
Adam
1
Lo envolvería en un cierre de ejecución automática con el viejo almacenado de antemano, con la primera línea de la función de reemplazo como algo similar if (typeof property === 'string' || typeof property === 'number' || typeof property === 'boolean') return oldIndexOf(property, value);. Esto se debe a que estos son los pocos tipos que son inmutables. También presentaría un tercer argumento para permitir el retroceso al método nativo si es necesario.
Isiah Meadows
8

Breve

myArray.indexOf('stevie','hello')

Casos de uso:

  /*****NORMAL****/  
[2,4,5].indexOf(4) ;//OUTPUT 1
 /****COMPLEX*****/
 [{slm:2},{slm:4},{slm:5}].indexOf(4,'slm');//OUTPUT 1
 //OR
 [{slm:2},{slm:4},{slm:5}].indexOf(4,function(e,i){
   return e.slm;
});//OUTPUT 1
/***MORE Complex**/
[{slm:{salat:2}},{slm:{salat:4}},{slm:{salat:5}}].indexOf(4,function(e,i){
   return e.slm.salat;
});//OUTPUT 1

API:

    Array.prototype.indexOfOld=Array.prototype.indexOf

    Array.prototype.indexOf=function(e,fn){
      if(!fn){return this.indexOfOld(e)}
      else{ 
       if(typeof fn ==='string'){var att=fn;fn=function(e){return e[att];}}
        return this.map(fn).indexOfOld(e);
      }
    };
Abdennour TOUMI
fuente
6

Hice algunas pruebas de rendimiento de varias respuestas aquí, que cualquiera puede ejecutar por sí mismo:

https://jsperf.com/find-index-of-object-in-array-by-contents

Basado en mis pruebas iniciales en Chrome, el siguiente método (usando un bucle for configurado dentro de un prototipo) es el más rápido:

Array.prototype.indexOfObject = function (property, value) {
    for (var i = 0, len = this.length; i < len; i++) {
        if (this[i][property] === value) return i;
    }
    return -1;
}

myArray.indexOfObject("hello", "stevie");

Este código es una versión ligeramente modificada de la respuesta de Nathan Zaetta.

En los puntos de referencia de rendimiento lo probé con el objetivo en el medio (índice 500) y el final (índice 999) de una matriz de 1000 objetos, e incluso si puse el objetivo como el último elemento de la matriz (lo que significa que tiene que recorrer cada uno de los elementos de la matriz antes de que se encuentre) todavía termina siendo el más rápido.

Esta solución también tiene el beneficio de ser una de las más concisas para ejecutar repetidamente, ya que solo la última línea necesita repetirse:

myArray.indexOfObject("hello", "stevie");
Unifónico
fuente
2
Estaba a punto de publicar una respuesta a esta pregunta usando un violín con mis propias pruebas, pero gracias a su respuesta, ya no tengo que hacerlo. Solo quiero confirmar sus pruebas: obtuve el mismo resultado, pero usando un whilebucle en lugar de foruno, y performance.now(). Me gustaría que esta respuesta fue upvoted cada vez que lo vi antes, me habría ahorrado algún tiempo ...
Yin Cognyto
5

Comparé varios métodos y recibí un resultado con la forma más rápida de resolver este problema. Es un forbucle. Es más de 5 veces más rápido que cualquier otro método.

Aquí está la página de la prueba: https://jsbench.me/9hjewv6a98

John Klimov
fuente
Perder un for-ofbucle no?
Douglas Gaskell
5

Mientras, la mayoría de las otras respuestas aquí son válidas. A veces, es mejor hacer una función simple y corta cerca de donde la usará.

// indexOf wrapper for the list of objects
function indexOfbyKey(obj_list, key, value) {
    for (index in obj_list) {
        if (obj_list[index][key] === value) return index;
    }
    return -1;
}
// Find the string in the list (default -1)
var test1 = indexOfbyKey(object_list, 'name', 'Stevie');
var test2 = indexOfbyKey(object_list, 'last_name', 'some other name');

Depende de lo que sea importante para ti. Puede guardar líneas de código y ser muy inteligente para usar una línea, o para poner una solución genérica en algún lugar que cubra varios casos extremos. Pero a veces es mejor decir: "aquí lo hice así" en lugar de dejar que los futuros desarrolladores tengan un trabajo adicional de ingeniería inversa. Especialmente si te consideras "un novato" como en tu pregunta.

SpiRail
fuente
4
array.filter(function(item, indx, arr){ return(item.hello === 'stevie'); })[0];

Cuidado con el [0] .

Es apropiado usar reducecomo enAntonio Laguna la respuesta.

Disculpas por la brevedad ...

Cody
fuente
4

Si su objeto es el mismo objeto que está utilizando dentro de la matriz, debería poder obtener el índice del objeto de la misma manera que lo haría como si fuera una cadena.

var hello = {
    hello: 'world',
    foo: 'bar'
};
var qaz = {
    hello: 'stevie',
    foo: 'baz'
}

var qazCLONE = { // new object instance and same structure
    hello: 'stevie',
    foo: 'baz'
}

var myArray = [hello,qaz];

myArray.indexOf(qaz) // should return 1
myArray.indexOf(qazCLONE) // should return -1
Caio Koiti
fuente
Esta es la respuesta que estaba buscando, ya que no estaba claro si IndexOf coincidía por valor o qué. Ahora sé que puedo usar IndexOf para encontrar mi objeto, y no preocuparme si hay otros objetos con las mismas propiedades.
MDave
3

simple:

myArray.indexOf(myArray.filter(function(item) {
    return item.hello == "stevie"
})[0])
buitre leonado
fuente
3

Si solo está interesado en encontrar el puesto, consulte la respuesta de @Pablo .

pos = myArray.map(function(e) { return e.hello; }).indexOf('stevie');

Sin embargo, si está ansioso por encontrar el elemento (es decir, si estaba pensando en hacer algo como esto myArray[pos]), hay una forma más eficiente de hacerlo en una línea , utilizando filter.

element = myArray.filter((e) => e.hello === 'stevie')[0];

Ver resultados de rendimiento (~ + 42% ops / seg): http://jsbench.github.io/#7fa01f89a5dc5cc3bee79abfde80cdb3

zurfyx
fuente
2

Vea este ejemplo: http://jsfiddle.net/89C54/

for (i = 0; i < myArray.length; i++) {
    if (myArray[i].hello === 'stevie') {
        alert('position: ' + i);
        return;
    }
}

Comienza a contar con cero.

Armin
fuente
2

Hice una función genérica para verificar que el siguiente es el código y funciona para cualquier objeto

function indexOfExt(list, item) {
    var len = list.length;

    for (var i = 0; i < len; i++) {
        var keys = Object.keys(list[i]);
        var flg = true;
        for (var j = 0; j < keys.length; j++) {
            var value = list[i][keys[j]];
            if (item[keys[j]] !== value) {
                flg = false;
            }
        }
        if (flg == true) {
            return i;
        }
    }
    return -1;
}

var items = [{ "hello": 'world', "foo": 'bar' }];
var selectedItem = { "hello": 'world', "foo": 'bar' };
alert(items.indexOf(selectedItem));
alert(indexOfExt(items, selectedItem));

La primera alerta devolverá -1 (significa coincidencia no encontrada) y la segunda alerta devolverá 0 (significa coincidencia encontrada).

Shiljo Paulson
fuente
2

Uso _.findIndexde la biblioteca underscore.js

Aquí está el ejemplo. _.findIndex([{a:1},{a: 2,c:10},{a: 3}], {a:2,c:10}) //1

niren
fuente
Si sugiere métodos de bibliotecas adicionales, debe mencionar de dónde provienen.
Craicerjack
@Steve Bennett buen uso. De la biblioteca ahora está en lodash
zabusa
2

Usando el findIndexmétodo ES6 , sin lodash ni ninguna otra biblioteca, puede escribir:

function deepIndexOf(arr, obj) {
  return arr.findIndex(function (cur) {
    return Object.keys(obj).every(function (key) {
      return obj[key] === cur[key];
    });
  });
}

Esto comparará las propiedades inmediatas del objeto, pero no se repetirá en las propiedades.

Si su implementación aún no proporciona findIndex(la mayoría no), puede agregar un polyfill ligero que admita esta búsqueda:

function deepIndexOf(arr, obj) {
  function findIndex = Array.prototype.findIndex || function (pred) {
    for (let i = 0; i < this.length; ++i) {
      if (pred.call(this, this[i], i)) {
        return i;
      }
    }

    return -1;
  }

  return findIndex.call(arr, function (cur) {
    return Object.keys(obj).every(function (key) {
      return obj[key] === cur[key];
    });
  });
}

(de mi respuesta en este engaño )

ssube
fuente
2

Puede utilizar una función nativa y conveniente Array.prototype.findIndex()básicamente:

El método findIndex () devuelve un índice en la matriz, si un elemento en la matriz satisface la función de prueba proporcionada. De lo contrario, se devuelve -1.

Solo una nota de que no es compatible con Internet Explorer, Opera y Safari, pero puede usar un Polyfill provisto en el siguiente enlace.

Más información:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex

var hello = {
  hello: 'world',
  foo: 'bar'
};
var qaz = {
  hello: 'stevie',
  foo: 'baz'
}

var myArray = [];
myArray.push(hello, qaz);

var index = myArray.findIndex(function(element, index, array) {
  if (element.hello === 'stevie') {
    return true;
  }
});
alert('stevie is at index: ' + index);

GibboK
fuente
2

Además de la respuesta de @Monika Garg , puedes usar findIndex()(Hay un polyfill para navegadores no soportados).

Vi que las personas rechazaron esta respuesta, y espero que lo hayan hecho debido a la sintaxis incorrecta, porque en mi opinión, esta es la forma más elegante.

El método findIndex () devuelve un índice en la matriz, si un elemento en la matriz satisface la función de prueba proporcionada. De lo contrario, se devuelve -1.

Por ejemplo:

var hello = {
  hello: 'world',
  foo: 'bar'
};
var qaz = {
  hello: 'stevie',
  foo: 'baz'
}

var myArray = [];
myArray.push(hello,qaz);

var index = myArray.findIndex(function(element) {
  return element.hello == 'stevie';
});

alert(index);

Mosh Feu
fuente
El método parece elegante, pero ¿no hay soporte para IE según MDN? developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Jaakko Karhu
Intente usar el polyfill (enlace en la respuesta).
Mosh Feu
1

Esta es la forma de encontrar el índice del objeto en la matriz

    var myArray = [{  hello: 'world',
        foo: 'bar'
    },{
        hello: 'stevie',
        foo: 'baz'
    }];



    for (i = 0; i < myArray.length; i++) {
        if (myArray[i].hello === 'stevie') {
            alert('position: ' + i);
            return;
        }
    }
Asad Fida
fuente
0

Esto funciona sin código personalizado

var arr, a, found;
arr = [{x: 1, y: 2}];
a = {x: 1, y: 2};
found = JSON.stringify(arr).indexOf(JSON.stringify(a)) > - 1;
// found === true

Nota: esto no proporciona el índice real, solo indica si su objeto existe en la estructura de datos actual

Xeltor
fuente
1
Esto no es válido ya que no permite obtener ningún índice
Antonio Laguna
0

Simplemente puedes usar

const someId = 2;
const array = [{id:1}, {id:2}, {id:3}];
const index = array.reduce((i, item, index) => item.id === someId ? index : i, -1);
alert('someId ' + someId + ' is at index ' + index);

Sin subrayado, no para, solo una reducción.

7ynk3r
fuente
0
var hello = {hello: "world",  foo: "bar"};
var qaz = {hello: "stevie", foo: "baz"};
var myArray = [];
myArray.push(hello,qaz);

function indexOfObject( arr, key, value   ) {
    var j = -1;
    var result = arr.some(function(obj, i) { 
        j++;
        return obj[key] == value;
    })

    if (!result) {
        return -1;
    } else {
        return j;
    };
}

alert(indexOfObject(myArray,"hello","world"));
user3235365
fuente
Utilice Array algún método.
user3235365
-1

Puede crear su propio prototipo para hacer esto:

algo como:

Array.prototype.indexOfObject = function (object) {
    for (var i = 0; i < this.length; i++) {
        if (JSON.stringify(this[i]) === JSON.stringify(object))
            return i;
    }
}
Janx de Venezuela
fuente
2
Mala práctica, romper la encapsulación: developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/…
HMR
Esto también se rompería en objetos definidos recursivamente.
Joseph Coco
-2

Preferiré usar el findIndex()método:

 var index = myArray.findIndex('hello','stevie');

index le dará el número de índice.

Monika Garg
fuente
1
Respuesta, ortografía y sangría de código y :) ¿están mal?
Sachin Verma
1
findIndex no está en ninguna implementación estándar de JavaScript. Hay una próxima propuesta (ecma 6) para tal método, pero su firma no es así. Por favor, aclare lo que quiere decir (tal vez el nombre del método), proporcione la declaración del método findIndex o nombre la biblioteca que está utilizando.
Sebastien F.