Localizar objeto por ID em uma matriz de objetos JavaScript

1546

Eu tenho uma matriz:

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

Não consigo alterar a estrutura da matriz. Estou recebendo um ID de 45e quero obter 'bar'esse objeto na matriz.

Como faço isso em JavaScript ou usando o jQuery?

thugsb
fonte

Respostas:

1192

Use o find()método:

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

Do MDN :

O find()método retorna o primeiro valor na matriz, se um elemento na matriz atender à função de teste fornecida. Caso contrário, undefinedé retornado.


Se você deseja encontrar seu índice , use findIndex():

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

Do MDN :

O findIndex()método retorna o índice do primeiro elemento na matriz que satisfaz a função de teste fornecida. Caso contrário, -1 é retornado.


Se você deseja obter uma matriz de elementos correspondentes, use o filter()método:

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

Isso retornará uma matriz de objetos. Se você deseja obter uma matriz de foopropriedades, pode fazer isso com o map()método:

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

Nota lateral: métodos como find()ou filter(), e funções de seta não são suportadas por navegadores mais antigos (como o IE); portanto, se você deseja oferecer suporte a esses navegadores, transpile seu código usando Babel (com o polyfill ).

Michał Perłakowski
fonte
2
Para várias condições de teste, seria, portanto, algo como: myArray.find (x => x.id === '45' && x.color == 'red').
Foo
2
Para mim, a melhor resposta até agora. Não precisa do jQuery nem cria novas matrizes auxiliares.
Canta
myArray.find (x => x.id === '45') não funciona no Mac PC
Govinda Rajbhar
@TJCrowder Eu não acho que seja uma boa ideia copiar e colar polyfills da MDN no seu código; em vez disso, você deve usar pacotes npm com polyfills. E a Babel inclui os recursos de polyfills para ES2015 +, no pacote babel-polyfill .
Michał Perłakowski
2
myArray.find (x => x.id === '45'). foo; lança uma exceção se não houver nenhum objeto com um ID '45'.
Frazer Kirkman
1466

Como você já está usando o jQuery, você pode usar a função grep que se destina a procurar uma matriz:

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

O resultado é uma matriz com os itens encontrados. Se você sabe que o objeto está sempre lá e que ocorre apenas uma vez, basta usar result[0].foopara obter o valor. Caso contrário, você deve verificar o comprimento da matriz resultante. Exemplo:

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
fonte
124
Seria mais seguro usar em ===vez de ==evitar problemas estranhos com o ==operador do JavaScript .
Vicky Chijwani
11
@VickyChijwani: Há algum problema ao comparar uma string com uma string?
Guffa
38
Bem, se você está absolutamente certo de que ambos e.ide idserá cordas, acho que está ok para uso ==. Mas se você não tiver certeza, poderá enfrentar problemas (já que '' == 0é truemas '' === 0é false). Sem mencionar, ===parece ser mais rápido ( stackoverflow.com/questions/359494/… ).
Vicky Chijwani
101
Basicamente, eu sempre uso ===porque funciona exatamente como ==em outras linguagens de programação. Considero ==inexistente em JavaScript.
Vicky Chijwani
6
@de. Muitas respostas aqui fornecem o comportamento pretendido ao procurar valores exclusivos; você pode reconhecê-los essencialmente pelo fato de que eles retornam ou interrompem seu loop mais cedo (ou instruem uma construção de nível inferior para parar a iteração). Veja a resposta de JaredPar para um exemplo canônico e o comentário de Aaronius sobre essa resposta para o mesmo insight. Em geral, as pessoas diferenciam as funções "filtro" e "localização" dessa maneira, mas a terminologia varia. Embora seja mais eficiente, ainda é uma pesquisa linear; portanto, se você quiser usar uma tabela de hash, consulte a resposta de Aaron Digulla (cuidado com os detalhes de impl.).
tne
362

Outra solução é criar um objeto de pesquisa:

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

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

Isso é especialmente interessante se você precisar fazer muitas pesquisas.

Isso não precisará de muito mais memória, pois os IDs e os objetos serão compartilhados.

Aaron Digulla
fonte
6
Exatamente o que eu estava procurando. Engraçado como eu estava tentando complicá-lo demais, tentando fazer um loop a cada vez, removendo cada item da lista como o encontrei quando eu precisava apenas alterar os dados recebidos do CouchDB e colocá-los em um formato que seja útil para meus clientes. necessidades. +1 senhor!
Slickplaid
5
isso é inteligente. Não consigo imaginar como os outros estavam convencidos olhando para todo o conjunto para cada uso.
Aladdin Mhemed
4
Desde que você não confie na ordem das propriedades: stackoverflow.com/questions/4886314/…
Marle1 1/05
Está usando uma pausa; no loop uma boa opção / melhoria, se você sabe que há apenas um objeto para encontrar?
precisa saber é
7
@irJvV: Não, isso não faz sentido. O código acima é útil se você precisar fazer muitas pesquisas. Se você olhar apenas uma vez, criar um lookupobjeto é uma perda de tempo.
Aaron Digulla
174

O ECMAScript 2015 fornece o método find () em matrizes:

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 sem bibliotecas externas. Mas se você deseja suporte para navegador mais antigo, inclua esse polyfill .

Rúnar Berg
fonte
1
Provavelmente porque ainda parece muito experimental e não é
suportado por
2
Isso pode ser simplificado para myArray.find(d=>d.id===45).foo;.
Shaggy
1
@ Shaggy ou mesmo myArray.find(({ id }) => id === 45).foo. Mas esta é uma resposta antiga que foi escrita antes da sintaxe do ES2015 ser tão suportada quanto agora. A resposta de @ Gothdo é atualmente a mais atualizada no segmento.
Rúnar Berg
1
@ Shaggy se o .find () retornar indefinido, sua otimização gerará um erro. Portanto, essa solução pode ser usada apenas nos casos em que uma correspondência é garantida.
Herbert Peters
1
@HerbertPeters Se você quer ter certeza de que você pode alway nula-check, que vai ser muito fácil com encadeamento opcional : myArray.find(d => d.id === 45)?.foo.
Rúnar Berg 03/04
141

O Underscore.js tem um bom método para isso:

myArray = [{'id':'73','foo':'bar'},{'id':'45','foo':'bar'},etc.]
obj = _.find(myArray, function(obj) { return obj.id == '45' })
GijsjanB
fonte
42
Para o registro, Lo-Dash (que geralmente é comprovadamente mais eficiente que Underscore) tem um método semelhante. Documentos aqui: lodash.com/docs#find
user456584
Se você espera apenas um objeto, o uso de findWhere seria mais eficiente, pois após encontrar um resultado, a pesquisa não avançaria mais.
sempre
@Foreever Nos documentos de _.find: "A função retorna assim que encontra um elemento aceitável e não percorre a lista inteira."
GijsjanB
129

Eu acho que a maneira mais fácil seria a seguinte, mas não funcionará no Internet Explorer 8 (ou 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
fonte
Estou curioso, há alguma vantagem de desempenho aqui em comparação com o habitual for?
Igor Zinov'yev 9/09/11
@Igor Zinov'yev: Sim, certamente há impactos no desempenho com essas ferramentas de matriz ES5. Uma função separada é executada para cada elemento, portanto, não será muito rápido em comparação com um forloop direto .
Pimvdb 09/09/11
Então você está dizendo que seria mais lento? Além disso, ele sempre examinará toda a matriz, tanto quanto eu posso ver, enquanto o forloop será encerrado na primeira correspondência.
Igor Zinov'yev 9/09/11
Se você precisar de suporte para o IE8, basta soltá-lo em: stackoverflow.com/questions/7153470/…
Adam Grant
Este código irá lançar um erro se não há nenhum elemento com queid
Stan
71

Tente o seguinte

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
fonte
17
Este não era digno de sua própria resposta, mas em navegadores modernos, esta solução pode ser escrita como: jsfiddle.net/rwaldron/j3vST
Rick
12
Se você estiver buscando eficiência, observe que este exemplo provavelmente é mais rápido do que usar filter () (veja o exemplo de Rick), pois este retorna quando encontra o primeiro item correspondente, enquanto filter () continua executando em toda a matriz mesmo depois de encontrar um partida. Este também não tem o custo de criar uma matriz adicional ou chamar uma função para cada item.
Aaronius
3
@ Rick, a coisa mais interessante sobre essa resposta é aparentemente você pode adicionar o console do firebug à janela de saída no jsFiddle. Isso é muito melhor do que registrar e dizer a outra pessoa para abrir o console para ver a saída. Impressionante!
precisa saber é o seguinte
1
Como ninguém o mencionou até agora, eu gostaria de acrescentar que o AngularJS também possui um método de filtro .
Eno
31

Uma versão genérica e mais flexível da função findById acima:

// 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');
será Farrell
fonte
15

Você pode obter isso facilmente usando a função 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";

Exemplo de trabalho: http://jsfiddle.net/hunter/Pxaua/

caçador
fonte
1
Esqueci o fato de que o jQuery mapremove automaticamente nullelementos. Parece enganador para mim e para o conceito comum de map, pois o resultado não é do mesmo tamanho da coleção original.
MaxArt
14

Você pode 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
fonte
1
@TobiasBeuving - O que usa Array.find () também é JS simples e deve parar na primeira descoberta, para que seja mais eficiente.
Adrian Lynch
12

Embora existam muitas respostas corretas aqui, muitas delas não tratam do fato de que essa é uma operação desnecessariamente cara se for feita mais de uma vez. Em um caso extremo, isso pode ser a causa de problemas reais de desempenho.

No mundo real, se você está processando muitos itens e o desempenho é uma preocupação, é muito mais rápido criar inicialmente uma pesquisa:

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

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

você pode obter itens em tempo fixo como este:

var bar = o[id];

Você também pode considerar usar um mapa em vez de um objeto como a pesquisa: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map

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

retorna o elemento do objeto se encontrado, caso contrário false

atraso
fonte
Apenas uma observação, Array.reduce não é suportado no IE8 e abaixo.
Burn_E99
7

Se você fizer isso várias vezes, poderá configurar um mapa (ES6):

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

Então você pode simplesmente fazer:

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

Eis como eu faria isso em JavaScript puro, da maneira mais minimalista possível, que funciona no ECMAScript 3 ou posterior. Ele retorna assim que uma correspondência é encontrada.

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
fonte
5

Mais genérico e curto

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

no seu caso Ex. var element = findFromArray(myArray,'id',45)isso lhe dará todo o elemento.

Savan Kaneriya
fonte
4

Você pode experimentar o Sugarjs em http://sugarjs.com/ .

Ele tem um método muito bom em Arrays .find,. Então você pode encontrar um elemento como este:

array.find( {id: 75} );

Você também pode passar um objeto com mais propriedades para adicionar outra "cláusula where".

Note que o Sugarjs estende objetos nativos, e algumas pessoas consideram isso muito ruim ...

chama profunda
fonte
2
Bem, é ruim, pois pode acontecer que novas versões do EcmaScript possam introduzir novos métodos com o mesmo nome. E adivinhem, foi exatamente isso que aconteceufind . Minha sugestão é que, se você deseja estender protótipos nativos, sempre use nomes mais específicos, deixando os mais simples para futuros desenvolvimentos padrão.
MaxArt #
esse comentário tem quase 2 anos e hoje eu prefiro usar o lodash de qualquer maneira. No entanto, se você quiser, pode ler sobre este tópico no site sugarjs. Eles defendem sua opinião: sugarjs.com/native
deepflame
1
O op se pedir especificamente para uma solução javascript ou jquery
Tobias Beuving
4

Com base na resposta aceita:

jQuery:

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

Ou CoffeeScript:

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

Recentemente, tenho que enfrentar a mesma coisa em que preciso pesquisar a string de uma enorme variedade.

Após algumas pesquisas, achei que seria fácil lidar com um código simples:

Código:

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

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

Serach a partir de 20k strings

maheshwaghmare
fonte
3

Iterar sobre qualquer item na matriz. Para cada item que você visita, verifique o ID desse item. Se for uma correspondência, devolva-a.

Se você quer apenas o 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
}

E a mesma coisa usando os métodos Array do 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
fonte
3

Enquanto o navegador suportar ECMA-262 , 5ª edição (dezembro de 2009), isso funcionará, quase uma linha:

var bFound = myArray.some(function (obj) {
    return obj.id === 45;
});
aggaton
fonte
2
Quase. bFoundé apenas um booleano que é truese um elemento satisfizer a condição necessária.
maxart
3

Você pode fazer isso mesmo em JavaScript puro usando a função "filtro" incorporada para matrizes:

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

Então agora simplesmente passe "id" no lugar de key"45" no lugar de value, e você obterá o objeto completo correspondente a um id de 45. Portanto,

myArr.filterObjects("id", "45");
kaizer1v
fonte
16
Não modifique objetos que você não possui.
Michał Perłakowski 14/02
3

Usar Array.prototype.filter() função

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

FILTRO

var getPerson = function(name){
    return jsonObj.filter(function(obj) {
      return obj.name === name;
    });
}
Sumit Ridhal
fonte
como posso pesquisar no objeto aninhado? Como pets = false deve retornar dois objetos.
Valay 14/06
use o .filtermétodo on obj.infono loop aninhado. var getPerson = function(name){ return jsonObj.filter(function(obj) { return obj.info.filter(function(info) { return pets === false; }); }); }
Sumit Ridhal
você pode usar o estilo es6 também im ... const filterData = jsonObj.filter (obj => obj.name === 'Alex')
DagicCross
3

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

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

ou

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

use a sintaxe ES6:

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

Ou use Lodash https://lodash.com/docs/4.17.10#filter , Sublinhado https://underscorejs.org/#filter

TLbiz
fonte
2

Eu realmente gostei da resposta fornecida por Aaron Digulla, mas precisava manter minha variedade de objetos para que pudesse iterá-la mais tarde. Então eu modifiquei 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
fonte
Utilizou a mesma solução mais rapidamente para encontrar itens na matriz. Mas parseInt é redundante aqui.
aleha 25/09/16
1

Usar:

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

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

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

Ele deve retornar um objeto por id.

volumexxx
fonte
você pode encurtar seu código usando return obj.id === 5? obj: false; Eu uso muito $ .each para iterar sobre matrizes.
marcel
@ marcel: Isso não vai funcionar. Como retornar false encerrará o loop, ele somente encontraria o objeto se fosse o primeiro item da matriz.
Guffa
1

Esta solução também pode 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");

Eu fiz exatamente como $.grepe se um objeto for descoberto, a função retornará o objeto, em vez de uma matriz.

soytian
fonte
2
Não modifique objetos que você não possui.
Michał Perłakowski 14/02
@ Gotico Eu concordo. Se alguém não souber function will return the object, rather than an arraypode cometer um erro, mas acho que depende dos usuários.
soytian 15/02/16
0

Partindo de resposta de aggaton , esta é uma função que realmente retorna o elemento queria (ou nullse não for encontrado), dada a arraye uma callbackfunção que retorna um valor truthy para o elemento "correta":

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

Lembre-se de que isso não funciona nativamente no IE8-, pois não suporta some. Um polyfill pode ser fornecido; alternativamente, há sempre o forloop clássico :

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

Na verdade, é mais rápido e mais compacto. Mas se você não quiser reinventar a roda, sugiro usar uma biblioteca de utilidades como sublinhado ou lodash.

MaxArt
fonte
0

Mais curto,

var theAnswerObj = _.findWhere(array, {id : 42});
Manu
fonte
1
Isso requer o uso da biblioteca sublinhado, o OP pediu um javascript simples ou solução jQuery
Tobias Beuving
2
depois de incluir o sublinhado, essa não é uma resposta curta!
Tim Ogilvy
-1

Considere "axesOptions" como uma matriz de objetos com um formato de objeto {: 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
fonte