Buscar objeto por id en una matriz de objetos JavaScript

1547

Tengo una matriz:

myArray = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}, etc.]

No puedo cambiar la estructura de la matriz. Me está pasando un id de 45, y quiero obtener 'bar'ese objeto en la matriz.

¿Cómo hago esto en JavaScript o usando jQuery?

thugsb
fuente

Respuestas:

1193

Usa el find()método:

myArray.find(x => x.id === '45').foo;

De MDN :

El find()método devuelve el primer valor en la matriz, si un elemento en la matriz satisface la función de prueba proporcionada. De undefinedlo contrario se devuelve.


Si quiere encontrar su índice , use findIndex():

myArray.findIndex(x => x.id === '45');

De MDN :

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


Si desea obtener una matriz de elementos coincidentes, utilice el filter()método en su lugar:

myArray.filter(x => x.id === '45');

Esto devolverá una matriz de objetos. Si desea obtener una matriz de foopropiedades, puede hacerlo con el map()método:

myArray.filter(x => x.id === '45').map(x => x.foo);

Nota al margen: métodos como find()o filter(), y las funciones de flecha no son compatibles con navegadores antiguos (como IE), por lo que si desea admitir estos navegadores, debe transpilar su código usando Babel (con el polyfill ).

Michał Perłakowski
fuente
2
Por lo tanto, para múltiples condiciones de prueba, sería algo como: myArray.find (x => x.id === '45' && x.color == 'red'). Foo
Apqu
2
Para mí, la mejor respuesta hasta ahora. No necesita jQuery ni crear nuevas matrices auxiliares.
Canta
myArray.find (x => x.id === '45') no funciona en Mac PC
Govinda Rajbhar
@TJCrowder No creo que sea una buena idea copiar y pegar polyfills de MDN en su código; en su lugar, debe usar paquetes npm con polyfills. Y Babel incluye polyfills para las funciones de ES2015 +, en el paquete babel-polyfill .
Michał Perłakowski
2
myArray.find (x => x.id === '45'). foo; lanza una excepción si no hay ningún objeto con una identificación de '45'.
Frazer Kirkman
1466

Como ya está usando jQuery, puede usar la función grep que está diseñada para buscar una matriz:

var result = $.grep(myArray, function(e){ return e.id == id; });

El resultado es una matriz con los elementos encontrados. Si sabe que el objeto siempre está ahí y que solo ocurre una vez, puede usarlo result[0].foopara obtener el valor. De lo contrario, debe verificar la longitud de la matriz resultante. Ejemplo:

if (result.length === 0) {
  // no result found
} else if (result.length === 1) {
  // property found, access the foo property using result[0].foo
} else {
  // multiple items found
}
Guffa
fuente
124
Sería más seguro usarlo en ===lugar de ==evitar problemas extraños con el ==operador de JavaScript .
Vicky Chijwani
11
@VickyChijwani: ¿Hay algún problema al comparar una cadena con una cadena?
Guffa
38
Bueno, si estás absolutamente seguro de que tanto e.idy idserá cuerdas, supongo que está bien para su uso ==. Pero si no está seguro, puede enfrentar problemas (ya que '' == 0es truepero '' === 0es false). Sin mencionar que ===parece ser más rápido ( stackoverflow.com/questions/359494/… ).
Vicky Chijwani
101
Básicamente siempre lo uso ===porque funciona exactamente como ==en otros lenguajes de programación. Considero ==que no existe en JavaScript.
Vicky Chijwani
66
@Delaware. Muchas respuestas aquí proporcionan el comportamiento deseado al buscar valores únicos; esencialmente puede reconocerlos por el hecho de que regresan o se rompen de su ciclo temprano (o instruir a una construcción de nivel inferior para que deje de iterar). Vea la respuesta de JaredPar para un ejemplo canónico, y el comentario de Aaronius sobre esa respuesta para la misma idea. En general, las personas diferencian entre las funciones "filtro" y "buscar" de esta manera, pero la terminología varía. Aunque es más eficiente, esta sigue siendo una búsqueda lineal, por lo que si desea utilizar una tabla hash, consulte la respuesta de Aaron Digulla (tenga cuidado con los detalles implícitos).
TNE
362

Otra solución es crear un objeto de búsqueda:

var lookup = {};
for (var i = 0, len = array.length; i < len; i++) {
    lookup[array[i].id] = array[i];
}

... now you can use lookup[id]...

Esto es especialmente interesante si necesita hacer muchas búsquedas.

Esto no necesitará mucha más memoria ya que las ID y los objetos serán compartidos.

Aaron Digulla
fuente
66
Exactamente lo que estaba buscando. Es curioso cómo intentaba complicarlo en exceso tratando de recorrerlo cada vez, eliminando cada elemento de la lista tal como lo encontré cuando solo necesitaba mutar los datos recibidos de CouchDB y ponerlos en un formato que sea útil para mi necesidades. +1 señor!
slickplaid
55
Esto es inteligente. No puedo imaginar cómo se convencieron los demás al mirar todo el conjunto para cada uso.
Aladdin Mhemed
44
Siempre y cuando no confíe en el orden de las propiedades: stackoverflow.com/questions/4886314/…
Marle1
Está usando un descanso; en el bucle una buena opción / mejora si sabe que solo hay un objeto para encontrar?
irJvV
77
@irJvV: No, eso no tiene ningún sentido. El código anterior es útil si necesita hacer muchas búsquedas. Si miras solo una vez, crear un lookupobjeto es una pérdida de tiempo.
Aaron Digulla
174

ECMAScript 2015 proporciona el método find () en matrices:

var myArray = [
 {id:1, name:"bob"},
 {id:2, name:"dan"},
 {id:3, name:"barb"},
]

// grab the Array item which matchs the id "2"
var item = myArray.find(item => item.id === 2);

// print
console.log(item.name);

Funciona sin bibliotecas externas. Pero si desea un soporte de navegador más antiguo , puede incluir este polyfill .

Rúnar Berg
fuente
1
Probablemente porque todavía parece muy experimental y no muchos navegadores lo admiten, developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
lejonl
2
Esto se puede simplificar a myArray.find(d=>d.id===45).foo;.
Shaggy
1
@Shaggy o incluso myArray.find(({ id }) => id === 45).foo. Pero esta es una vieja respuesta que se escribió antes de que la sintaxis de ES2015 fuera tan compatible como ahora. La respuesta de @ Gothdo es actualmente la más actualizada en el hilo.
Rúnar Berg
1
@Shaggy si .find () devuelve indefinido, entonces su optimización arroja un error. Por lo tanto, esta solución solo se puede utilizar en casos en los que se garantice una coincidencia.
Herbert Peters
1
@HerbertPeters Si usted quiere estar seguro de que todos los días se pueden nula-cheque, que va a ser muy fácil con el encadenamiento opcional : myArray.find(d => d.id === 45)?.foo.
Rúnar Berg
141

Underscore.js tiene un buen método para eso:

myArray = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'},etc.]
obj = _.find(myArray, function(obj) { return obj.id == '45' })
GijsjanB
fuente
42
Para el registro, Lo-Dash (que a menudo es demostrablemente más eficiente que el subrayado) tiene un método similar. Documentos aquí: lodash.com/docs#find
user456584
Si espera un solo objeto, entonces usar findWhere sería más eficiente ya que después de encontrar un resultado, la búsqueda no iría más allá.
Foreever
@Foreever De los documentos de _.find: "La función vuelve tan pronto como encuentra un elemento aceptable y no recorre toda la lista".
GijsjanB
129

Creo que la forma más fácil sería la siguiente, pero no funcionará en Internet Explorer 8 (o anterior):

var result = myArray.filter(function(v) {
    return v.id === '45'; // Filter out the appropriate one
})[0].foo; // Get result and access the foo property
pimvdb
fuente
Tengo curiosidad, ¿hay alguna ventaja de rendimiento aquí en comparación con lo habitual for?
Igor Zinov'yev
@Igor Zinov'yev: Sí, ciertamente hay impactos en el rendimiento con esas herramientas de matriz ES5. Se ejecuta una función separada para cada elemento, por lo que no será realmente rápida en comparación con un forbucle directo .
pimvdb
¿Entonces estás diciendo que sería más lento? Además, siempre escaneará toda la matriz, hasta donde puedo ver, mientras que el forbucle terminará en la primera coincidencia.
Igor Zinov'yev
Si necesita soporte para IE8, simplemente colóquelo en: stackoverflow.com/questions/7153470/…
Adam Grant
Este código arrojará un error si no hay ningún elemento con esoid
Stan
71

Intenta lo siguiente

function findById(source, id) {
  for (var i = 0; i < source.length; i++) {
    if (source[i].id === id) {
      return source[i];
    }
  }
  throw "Couldn't find object with id: " + id;
}
JaredPar
fuente
17
Esto no era digno de su propia respuesta, pero en los navegadores modernos esta solución se puede escribir como: jsfiddle.net/rwaldron/j3vST
Rick
12
Si busca eficiencia, tenga en cuenta que este ejemplo es probablemente más rápido que usar filter () (vea el ejemplo de Rick) ya que este regresa una vez que encuentra el primer elemento coincidente, mientras que filter () continúa ejecutándose a través de la matriz completa incluso después de encontrar un partido. Este tampoco tiene el costo de crear una matriz adicional o llamar a una función para cada elemento.
Aaronius
3
@ Rick, lo más interesante de esa respuesta es que aparentemente puedes agregar la consola firebug a la ventana de salida en jsFiddle. Esto es mucho mejor que iniciar sesión y decirle a otra persona que abra la consola para ver el resultado. ¡Increíble!
KyleMit
1
Como nadie lo ha mencionado hasta ahora, quería agregar que AngularJS también tiene un método de filtro .
Eno
31

Una versión genérica y más flexible de la función findById anterior:

// array = [{key:value},{key:value}]
function objectFindByKey(array, key, value) {
    for (var i = 0; i < array.length; i++) {
        if (array[i][key] === value) {
            return array[i];
        }
    }
    return null;
}

var array = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}];
var result_obj = objectFindByKey(array, 'id', '45');
Will Farrell
fuente
15

Puede obtener esto fácilmente usando la función map () :

myArray = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}];

var found = $.map(myArray, function(val) {
    return val.id == 45 ? val.foo : null;
});

//found[0] == "bar";

Ejemplo de trabajo: http://jsfiddle.net/hunter/Pxaua/

cazador
fuente
1
Olvidé el hecho de que jQuery's mapelimina automáticamente nullelementos. Suena engañoso para mí y para el concepto común de map, ya que el resultado no es de la misma longitud de la colección original.
MaxArt
14

Puedes usar filtros,

  function getById(id, myArray) {
    return myArray.filter(function(obj) {
      if(obj.id == id) {
        return obj 
      }
    })[0]
  }

get_my_obj = getById(73, myArray);
Joe Lewis
fuente
1
@TobiasBeuving: el que usa Array.find () también es JS simple y debería detenerse en el primer hallazgo, por lo que será más eficiente.
Adrian Lynch
12

Si bien hay muchas respuestas correctas aquí, muchas de ellas no abordan el hecho de que esta es una operación innecesariamente costosa si se realiza más de una vez. En un caso extremo, esto podría ser la causa de problemas reales de rendimiento.

En el mundo real, si está procesando muchos elementos y el rendimiento es una preocupación, es mucho más rápido crear inicialmente una búsqueda:

var items = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'}];

var lookup = items.reduce((o,i)=>o[i.id]=o,{});

luego puede obtener artículos en un tiempo fijo como este:

var bar = o[id];

También puede considerar usar un Mapa en lugar de un objeto como búsqueda: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map

Tom
fuente
11

Usando nativo Array.reduce

var array = [ {'id':'73' ,'foo':'bar'} , {'id':'45' ,'foo':'bar'} , ];
var id = 73;
var found = array.reduce(function(a, b){
    return (a.id==id && a) || (b.id == id && b)
});

devuelve el elemento objeto si se encuentra, de lo contrario false

laggingreflex
fuente
Solo una nota, Array.reduce no es compatible con IE8 y versiones inferiores.
Burn_E99
7

Si hace esto varias veces, puede configurar un Mapa (ES6):

const map = new Map( myArray.map(el => [el.id, el]) );

Entonces simplemente puedes hacer:

map.get(27).foo
Jonas Wilms
fuente
6

Así es como lo haría en JavaScript puro, de la manera más mínima que pueda pensar que funciona en ECMAScript 3 o posterior. Regresa tan pronto como se encuentre una coincidencia.

var getKeyValueById = function(array, key, id) {
    var testArray = array.slice(), test;
    while(test = testArray.pop()) {
        if (test.id === id) {
            return test[key];
        }
    }
    // return undefined if no matching id is found in array
    return;
}

var myArray = [{'id':'73', 'foo':'bar'}, {'id':'45', 'foo':'bar'}]
var result = getKeyValueById(myArray, 'foo', '45');

// result is 'bar', obtained from object with id of '45'
Dan W
fuente
5

Más genérico y corto

function findFromArray(array,key,value) {
        return array.filter(function (element) {
            return element[key] == value;
        }).shift();
}

en su caso Ej. var element = findFromArray(myArray,'id',45)eso te dará todo el elemento.

Savan Kaneriya
fuente
4

Puedes probar Sugarjs desde http://sugarjs.com/ .

Tiene un método muy dulce en matrices, .find. Entonces puedes encontrar un elemento como este:

array.find( {id: 75} );

También puede pasarle un objeto con más propiedades para agregar otra "cláusula where".

Tenga en cuenta que Sugarjs extiende objetos nativos, y algunas personas consideran esto muy malvado ...

llama profunda
fuente
2
Bueno, es malo, ya que puede suceder que las nuevas versiones de EcmaScript puedan introducir nuevos métodos con el mismo nombre. Y adivina qué, esto es exactamente lo que sucedió confind . Mi sugerencia es que si desea ampliar los prototipos nativos, utilice siempre nombres más específicos, dejando los más simples para futuros desarrollos estándar.
MaxArt
este comentario tiene casi 2 años y hoy prefiero usar lodash de todos modos. Sin embargo, si lo desea, puede leer sobre este tema en el sitio web sugarjs. Consideran bien su opinión: sugarjs.com/native
deepflame
1
El
operador
4

Sobre la base de la respuesta aceptada:

jQuery:

var foo = $.grep(myArray, function(e){ return e.id === foo_id})
myArray.pop(foo)

O CoffeeScript:

foo = $.grep myArray, (e) -> e.id == foo_id
myArray.pop foo
stevenspiel
fuente
4

Recientemente, tengo que enfrentar la misma cosa en la que necesito buscar la cadena desde una gran matriz.

Después de buscar, encontré que será fácil de manejar con un código simple:

Código:

var items = mydata.filter(function(item){
    return item.word.toLowerCase().startsWith( 'gk );
})

Ver https://jsfiddle.net/maheshwaghmare/cfx3p40v/4/

Serach de 20k cuerdas

maheshwaghmare
fuente
3

Iterar sobre cualquier elemento de la matriz. Por cada artículo que visite, verifique la identificación de ese artículo. Si es un partido, devuélvelo.

Si solo quieres el codez:

function getId(array, id) {
    for (var i = 0, len = array.length; i < len; i++) {
        if (array[i].id === id) {
            return array[i];
        }
    }
    return null; // Nothing found
}

Y lo mismo con los métodos de matriz de ECMAScript 5:

function getId(array, id) {
    var obj = array.filter(function (val) {
        return val.id === id;
    });

    // Filter returns an array, and we just want the matching item.
    return obj[0];
}
Zirak
fuente
3

Mientras el navegador sea compatible con ECMA-262 , 5a edición (diciembre de 2009), esto debería funcionar, casi de una sola línea:

var bFound = myArray.some(function (obj) {
    return obj.id === 45;
});
aggaton
fuente
2
Casi. bFoundes solo un booleano que es truesi un elemento cumple la condición requerida.
MaxArt
3

Puede hacer esto incluso en JavaScript puro utilizando la función integrada "filtro" para las matrices:

Array.prototype.filterObjects = function(key, value) {
    return this.filter(function(x) { return x[key] === value; })
}

Así que ahora simplemente pase "id" en lugar de key"45" en lugar de value, y obtendrá el objeto completo que coincide con un id de 45. Entonces eso sería,

myArr.filterObjects("id", "45");
kaizer1v
fuente
16
No modifique objetos que no le pertenecen.
Michał Perłakowski
3

Utiliza la Array.prototype.filter()función.

DEMO : https://jsfiddle.net/sumitridhal/r0cz0w5o/4/

JSON

var jsonObj =[
 {
  "name": "Me",
  "info": {
   "age": "15",
   "favColor": "Green",
   "pets": true
  }
 },
 {
  "name": "Alex",
  "info": {
   "age": "16",
   "favColor": "orange",
   "pets": false
  }
 },
{
  "name": "Kyle",
  "info": {
   "age": "15",
   "favColor": "Blue",
   "pets": false
  }
 }
];

FILTRAR

var getPerson = function(name){
    return jsonObj.filter(function(obj) {
      return obj.name === name;
    });
}
Sumit Ridhal
fuente
¿Cómo puedo buscar dentro de un objeto anidado? Al igual que mascotas = falso debería devolver dos objetos.
Valay
use el .filtermétodo en obj.infoen bucle anidado. var getPerson = function(name){ return jsonObj.filter(function(obj) { return obj.info.filter(function(info) { return pets === false; }); }); }
Sumit Ridhal
podrías usar el estilo es6 también imo ... const filterData = jsonObj.filter (obj => obj.name === 'Alex')
DagicCross
3

Podemos usar los métodos de Jquery $.each()/$.grep()

var data= [];
$.each(array,function(i){if(n !== 5 && i > 4){data.push(item)}}

o

var data = $.grep(array, function( n, i ) {
  return ( n !== 5 && i > 4 );
});

use la sintaxis ES6:

Array.find, Array.filter, Array.forEach, Array.map

O utilice Lodash https://lodash.com/docs/4.17.10#filter , subrayado https://underscorejs.org/#filter

TLbiz
fuente
2

Realmente me gustó la respuesta proporcionada por Aaron Digulla, pero necesitaba mantener mi conjunto de objetos para poder repetirlo más tarde. Entonces lo modifiqué para

	var indexer = {};
	for (var i = 0; i < array.length; i++) {
	    indexer[array[i].id] = parseInt(i);
	}
	
	//Then you can access object properties in your array using 
	array[indexer[id]].property

quincyaft
fuente
Se utilizó la misma solución que la más rápida para buscar elementos en la matriz. Pero parseInt es redundante aquí.
aleha
1

Utilizar:

var retObj ={};
$.each(ArrayOfObjects, function (index, obj) {

        if (obj.id === '5') { // id.toString() if it is int

            retObj = obj;
            return false;
        }
    });
return retObj;

Debe devolver un objeto por id.

volumexxx
fuente
podría acortar su código utilizando return obj.id === 5? obj: falso; Uso $ .each mucho para iterar sobre matrices.
marcel
@marcel: Eso no funcionará. Como devolver falso finalizará el ciclo, solo encontrará el objeto si fuera el primer elemento de la matriz.
Guffa
1

Esta solución también puede ser útil:

Array.prototype.grep = function (key, value) {
    var that = this, ret = [];
    this.forEach(function (elem, index) {
        if (elem[key] === value) {
            ret.push(that[index]);
        }
    });
    return ret.length < 2 ? ret[0] : ret;
};
var bar = myArray.grep("id","45");

Lo hice así $.grepy si un objeto se descubre, la función devolverá el objeto, en lugar de una matriz.

soytian
fuente
2
No modifique objetos que no le pertenecen.
Michał Perłakowski
@ Gothdo Estoy de acuerdo. Si alguien no sabe function will return the object, rather than an arraypuede tener un error, pero creo que depende de los usuarios.
Soytiano
0

A partir de la respuesta de aggaton , esto es en realidad una función que devuelve el elemento deseado (o nullsi no se encuentra), dado el arrayy una callbackfunción que devuelve un valor Truthy para el elemento "correcta":

function findElement(array, callback) {
    var elem;
    return array.some(function(e) {
        if (callback(e)) {
            elem = e;
            return true;
        }
    }) ? elem : null;
});

Solo recuerda que esto no funciona de forma nativa en IE8-, ya que no es compatible some. Se puede proporcionar un polyfill, alternativamente, siempre existe el forciclo clásico :

function findElement(array, callback) {
    for (var i = 0; i < array.length; i++)
        if (callback(array[i])) return array[i];
    return null;
});

En realidad es más rápido y más compacto. Pero si no desea reinventar la rueda, le sugiero que use una biblioteca de utilidades como guión bajo o lodash.

MaxArt
fuente
0

Más corto

var theAnswerObj = _.findWhere(array, {id : 42});
Manu
fuente
1
Esto requiere el uso de la biblioteca de subrayado, el OP solicitó una solución simple de JavaScript o jQuery
Tobias Beuving
2
una vez que incluya guión bajo, ¡esta no es una respuesta corta!
Tim Ogilvy
-1

Considere "axesOptions" como una matriz de objetos con un formato de objeto que es {: field_type => 2,: fields => [1,3,4]}

function getFieldOptions(axesOptions,choice){
  var fields=[]
  axesOptions.each(function(item){
    if(item.field_type == choice)
        fields= hashToArray(item.fields)
  });
  return fields;
}
ramya
fuente