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.


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

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

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

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

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

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

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

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

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

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.


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 ...


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

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

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


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.


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


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


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 )


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


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


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

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


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.


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

Utilice Array algún método.
user3235365


-2

Preferiré usar el findIndex()método:

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

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


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.
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.