método indexOf en una matriz de objetos?

617

¿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 existe un método simple o si debería construir mi propia función para hacer eso.

5
  • 1
    ¿Quieres fusionar los dos objetos helloy qaz? Armin 29/12/11 a las 13:01
  • No, no lo hago. Quiero tener una lista de objetos en una matriz. Antonio Laguna 29/12/11 a las 13:05
  • ¡Ah bien! Desea saber la posición de todo el objeto en la matriz, que tiene una propiedad definida. Armin 29/12/11 a las 13:06
  • 12
    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]; [enlace] ( stackoverflow.com/a/16100446/1937255 )Rick 30/07/2015 a las 0:21
  • ES6 Array.indexOf es mejor que la respuesta aceptada (si ES6 funciona para usted) - vea el ejemplo completo a continuaciónyar1 31 de enero de 2017 a las 6:44
1214

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

pos = myArray.map(function(e) { return e.hello; }).indexOf('stevie');
19
  • 62
    Honestamente, esta debería ser la respuesta aceptada. La mayoría de los navegadores admiten hoy en díaArray.prototype.map()AlbertEngelB 27/06/2013 a las 21:06
  • 10
    No es compatible con IE8 pero, si eso no es un problema, esta es la mejor solución. Antonio Laguna 22/11/2013 a las 11:56
  • 72
    Um ... ¿no vale la pena señalar que Array.prototype.map () crea una matriz completamente nueva que contiene los elementos mapeados? Entonces, si tiene una matriz con 1000 elementos, primero ha creado otra matriz con 1000 elementos y luego la busca. Creo que valdría la pena ver el rendimiento de este método frente a un bucle for simple. Especialmente cuando se ejecuta en una plataforma móvil con recursos limitados. Doug 17 de agosto de 2015 a las 5:51
  • 7
    @Doug Aunque su punto sobre el rendimiento es realmente 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 perfilado para los cuellos de botella? Jared Smith 4 nov 15 a las 17:12
  • 11
    Técnicamente, un archivo js minificado también es una línea; DgreaterKing 3 de mayo de 2017 a las 15:45
445

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

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

La solución con 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 forma 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 en 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
4
  • la función tiene la comparación de término de búsqueda incorrecta, ya que debería ser término de búsqueda :)Antonio Laguna 29/12/11 a las 14:50
  • hubo múltiples ocurrenciasJoe 27/0214 a las 21:06
  • 3
    @SteveBennett es una versión optimizada para el rendimiento; 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 comprueba de nuevo en cada iteración. Consulte también stackoverflow.com/questions/5349425/… y stackoverflow.com/questions/8452317/… Sin embargo, si el rendimiento no es alto, en realidad no importa. loother 20/04/2016 a las 15:09
  • 1
    Buena respuesta, pero hice algunas evaluaciones comparativas de rendimiento (consulte jsperf.com/find-index-of-object-in-array-by-contents ) y descubrí que la respuesta basada en funciones que se menciona aquí parece ser la segunda respuesta con mayor rendimiento. Lo único más eficaz termina siendo ponerlo en un prototipo, en lugar de solo en una función, como mencioné en mi respuesta . Uniphonic 27 de junio de 2017 a las 23:33
144

En ES2015, esto es bastante fácil:

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

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

myArray.findIndex(x => x.hello === 'stevie')
4
  • 2
    Buen enfoque para usar ES6kag 20 de septiembre de 2016 a las 6:36
  • Me sorprendió que ninguno de estos métodos sea tan eficaz como el bucle for prototipo, 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í termina con un rendimiento menor? Vea el enlace en mi respuesta para ver los puntos de referencia. Uniphonic 27/06/2017 a las 22:32
  • Es bueno saber si el rendimiento es importante para la tarea en cuestión. Rara vez lo es, en mi experiencia, pero mmm. Steve Bennett 29 de junio de 2017 a las 3:49
  • @Uniphonic - año 2021 findIndex si es compatible con todos los navegadores, excepto OperaMini e IE - caniuse.com/array-find-indexMauricio Gracia Gutierrez 30 de junio a las 13:37
24
var idx = myArray.reduce( function( cur, val, index ){

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

}, -1 );
0
17

Me gusta la respuesta de Pablo, pero Array # indexOf y Array # map no funcionan en todos los navegadores. Underscore usará código nativo si está disponible, pero también tiene alternativas. 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();
5
  • 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 30 de septiembre de 2014 a las 15:02
  • 1
    Podrías usar un polyfill. . . o simplemente puede usar guión bajo o lodash, que son básicamente polyfills que tienen un montón de otras ventajas adjuntas . ¿Cuál es la objeción con subrayado? ¿Tamaño? tandrewnichols 1/10/2014 a las 11:45
  • Realmente me gusta Underscore, tu respuesta también es inteligente, pero en mi humilde opinión, la respuesta de Pablo es la más limpia. Johann Echavarria 2/10/2014 a las 5:35
  • Vaya, nunca pensé en usar encadenamiento así. Realmente me gusta cómo hace que la búsqueda sea fluida. Dylan Pierce 20/04/2016 a las 20:12
  • chaines superfluo aquí. _.pluck(myArray, 'hello').indexOf('stevie')Steve Bennett 15 de agosto de 2018 a las 6:58
14

O prototipalo:

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");
2
  • 9
    ¡Muy conveniente! Aunque elegiría prototype.indexOfObject para no interferir con el método Array.indexOf existente. 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 30/10/2013 a las 11:42
  • 1
    Lo envolvería en un cierre autoejecutable con el antiguo almacenado de antemano, con la primera línea de la función de reemplazo como algo parecido a 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 habilitar el respaldo al método nativo si es necesario. Isiah Meadows 27 de mayo de 2014 a las 4:52
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);
      }
    };
8

Comparé varios métodos y obtuve 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

2
  • Falta un for-ofbucle, ¿no? Douglas Gaskell 3 oct 2019 a las 0:11
  • Lástima que la gente no desplace dos respuestas hacia abajo. Ésta es la única buena respuesta. Gracias. HenrijsS 15 de marzo a las 20:23
8

Si bien, la mayoría de las otras respuestas aquí son válidas. A veces, es mejor hacer una función corta y simple 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 ahorrar líneas de código y ser muy inteligente para usar una sola 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 simplemente: "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.

7

Si su objeto es el mismo objeto de los que está usando 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
1
  • 1
    Esta es la respuesta que estaba buscando, ya que no tenía 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 10 de mayo de 2020 a las 0:13
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

Según 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 muy al final (índice 999) de una matriz de 1000 objetos, e incluso si coloco el objetivo como el último elemento de la matriz (es decir que tiene que recorrer cada elemento de la matriz antes de que se encuentre) aún termina siendo el más rápido.

Esta solución también tiene la ventaja de ser una de las más concisas para ejecutar repetidamente, ya que solo es necesario repetir la última línea:

myArray.indexOfObject("hello", "stevie");
1
  • 2
    Estaba a punto de publicar una respuesta a esta pregunta usando un violín con mis propias pruebas, pero gracias a tu 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(). Desearía que esta respuesta tuviera más votos positivos y que la vi antes, me habría ahorrado algo de tiempo ...Yin Cognyto 5 de diciembre de 2017 a las 15:48
4
array.filter(function(item, indx, arr){ return(item.hello === 'stevie'); })[0];

Cuidado con el [0].

Es apropiado usar reducecomo en Antonio Lagunala respuesta.

Disculpas por la brevedad ...

0
3

Prueba esto:

console.log(Object.keys({foo:"_0_", bar:"_1_"}).indexOf("bar"));

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/keys

3

sencillo:

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

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

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

Sin embargo, si está deseando encontrar el elemento (es decir, si estaba pensando en hacer algo como esto myArray[pos]), existe 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% operaciones / segundo): 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;
    }
}

Empieza a contar con cero.

2

Hice una función genérica para verificar que a continuación se encuentra 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

Usar _.findIndexde la biblioteca de underscore.js

Este es el ejemplo _.findIndex([{a:1},{a: 2,c:10},{a: 3}], {a:2,c:10}) //1

2
  • Si sugiere métodos de bibliotecas adicionales, debe mencionar de dónde provienen. Craicerjack 24/06/15 a las 10:01
  • @Steve Bennett buen uso. De la biblioteca ahora está en lodashzabusa 21/07/2016 a las 9:03
2

Usando el findIndexmétodo ES6 , sin lodash o cualquier 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 recurrirá a las propiedades.

Si su implementación aún no lo proporciona findIndex(la mayoría no lo hace), 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 sobre este engaño )

0
2

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

The findIndex() method returns an index in the array, if an element in the array satisfies the provided testing function. Otherwise -1 is returned.

Solo una nota: no es compatible con Internet Explorer, Opera y Safari, pero puede usar un Polyfill proporcionado en el enlace a continuación.

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

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

Vi que la gente rechazó 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.

The findIndex() method returns an index in the array, if an element in the array satisfies the provided testing function. Otherwise -1 is returned.

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

Esta es la forma de encontrar el índice del objeto en una 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

Note: this does not give the actual index, it only tells if your object exists in the current data structure

1
  • 1
    Esto no es válido ya que no permite obtener ningún índiceAntonio Laguna 3 de agosto de 2015 a las 5:12
0

Simplemente puede 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 guión bajo, 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"));
1
  • Utilice Array algún método. user3235365 5/11/2017 a las 22:16
0

La mayoría de las respuestas aquí no resuelven todos los casos. Encontré esta solución mejor:

const isInarray = myArr.filter((obj) => obj.hello === 'stevie' && obj.foo === 'baz').length > 0;
if (!isInArray) {
 ....
}
-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;
    }
}
2
-2

Preferiré usar el findIndex()método:

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

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

2
  • 1
    La respuesta, la ortografía y la sangría del código y :) ¿son incorrectos? Sachin Verma 4 de febrero de 2014 a las 6:16
  • 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. 22/03/2014 a las 18:19