Como encontrar um valor em uma matriz de objetos em JavaScript?

90

Eu tenho uma série de objetos:

Object = {
   1 : { name : bob , dinner : pizza },
   2 : { name : john , dinner : sushi },
   3 : { name : larry, dinner : hummus }
}

Eu quero ser capaz de pesquisar o objeto / matriz para onde a chave é "jantar" e ver se ela corresponde a "sushi".

Eu sei que o jQuery tem $ .inArray, mas não parece funcionar em matrizes de objetos. Ou talvez eu esteja errado. indexOf também parece funcionar apenas em um nível de array.

Não há função ou código existente para isso?

Questionador
fonte
Isso já foi perguntado antes. Você deve escrever sua própria função ou usar alguma outra biblioteca.
Felix Kling,
1
Observe que Objectestá reservado em Javascript, Objecté o objeto objeto, ou seja, a mãe de todos os objetos.
adamse
1
a pergunta e a resposta aceita não estão relacionadas a matrizes multidimensionais, mas mais à filtragem de matrizes unidimensionais pelos valores de propriedade de seus itens. => Eles não resolveram meu problema de "encontrar um valor em uma matriz multidimensional".
Martin Schneider

Respostas:

211

Se você tiver uma matriz como

var people = [
  { "name": "bob", "dinner": "pizza" },
  { "name": "john", "dinner": "sushi" },
  { "name": "larry", "dinner": "hummus" }
];

Você pode usar o filtermétodo de um objeto Array:

people.filter(function (person) { return person.dinner == "sushi" });
  // => [{ "name": "john", "dinner": "sushi" }]

Em implementações de JavaScript mais recentes, você pode usar uma expressão de função:

people.filter(p => p.dinner == "sushi")
  // => [{ "name": "john", "dinner": "sushi" }]

Você pode pesquisar por pessoas que "dinner": "sushi"usam ummap

people.map(function (person) {
  if (person.dinner == "sushi") {
    return person
  } else {
    return null
  }
}); // => [null, { "name": "john", "dinner": "sushi" }, null]

ou um reduce

people.reduce(function (sushiPeople, person) {
  if (person.dinner == "sushi") {
    return sushiPeople.concat(person);
  } else {
    return sushiPeople
  }
}, []); // => [{ "name": "john", "dinner": "sushi" }]

Tenho certeza que você é capaz de generalizar isso para chaves e valores arbitrários!

adamse
fonte
7
Lembre-se de que essas soluções fazem parte do ECMAScript 5 e não são suportadas no IE8. kangax.github.com/es5-compat-table Por mais que eu prefira a resposta de @adamse, a de alex é mais amigável para "navegador de merda velho". Porém, não tenho certeza sobre o desempenho.
EasyCo
@SalmanA - nem a questão ou a solução está se referindo ao jQuery. O filtro javascript () é usado, não o jQuery-specific $ .filter () - tutorialspoint.com/javascript/array_filter.htm
Tapirboy
Conforme mencionado por EasyCo, a função de filtro não é compatível com o IE8. No entanto, ele é facilmente adicionado ao protótipo do Array e, portanto, utilizável em qualquer navegador com uma pequena função no início de seus scripts. Isso é descrito na documentação do filtro . Ele fornece a função exata especificada em ECMA-262, então é literalmente a mesma coisa.
dallin
1
Acabei de adicionar uma resposta que usa o grepmétodo jQuery . Pode fazer sentido rolar em sua resposta, pois é o mesmo conceito que você está fazendo, mas dependente de jQuery e amigável para o navegador de merda.
Zach Lysobey
Existe um motivo pelo qual continuo recebendo um erro de referência com minha variável de retorno? Tentei as duas primeiras respostas retornando "x '' e continua dizendo que é indefinido ...
Evan Lalo
18

jQuery tem um método embutido jQuery.grepque funciona de forma semelhante à filterfunção ES5 da resposta de @adamse e deve funcionar bem em navegadores mais antigos.

Usando o exemplo de adamse:

var peoples = [
  { "name": "bob", "dinner": "pizza" },
  { "name": "john", "dinner": "sushi" },
  { "name": "larry", "dinner": "hummus" }
];

você pode fazer o seguinte

jQuery.grep(peoples, function (person) { return person.dinner == "sushi" });
  // => [{ "name": "john", "dinner": "sushi" }]
Zach Lysobey
fonte
10
var getKeyByDinner = function(obj, dinner) {
    var returnKey = -1;

    $.each(obj, function(key, info) {
        if (info.dinner == dinner) {
           returnKey = key;
           return false; 
        };   
    });

    return returnKey;       

}

jsFiddle .

Contanto que -1nunca seja uma chave válida.

alex
fonte
quase aprovou este, mas não havia explicação no post.
mickmackusa
10

Se você vai fazer essa pesquisa com frequência, considere alterar o formato do seu objeto para que o jantar seja realmente uma chave. Isso é como atribuir uma chave primária de cluster em uma tabela de banco de dados. Então, por exemplo:

Obj = { 'pizza' : { 'name' : 'bob' }, 'sushi' : { 'name' : 'john' } }

Agora você pode acessá-lo facilmente assim: Object['sushi']['name']

Ou se o objeto é realmente tão simples (apenas 'nome' no objeto), você pode apenas alterá-lo para:

Obj = { 'pizza' : 'bob', 'sushi' : 'john' }

E então acessá-lo como: Object['sushi'].

Obviamente, nem sempre é possível ou vantajoso reestruturar seu objeto de dados dessa maneira, mas a questão é que, às vezes, a melhor resposta é considerar se seu objeto de dados está estruturado da melhor maneira. Criar uma chave como essa pode ser mais rápido e criar um código mais limpo.

dallin
fonte
1
Amo sua resposta, mas achei um problema com a sintaxe: o meu funcionava apenas assim: obj = {"pizza": {"name": "bob"}, "sushi": {"name": "john"}} alert ( obj ['pizza'] ['nome']); mas ainda. obrigado! encontrei o que estava procurando! )
aleXela
@alexela Obrigado alexela! Atualizei minha resposta com sua observação astuta! Eu tinha acabado de copiar o exemplo no post do OP e esquecido de adicionar as aspas, mas você está certo - não funcionará a menos que haja aspas ao redor dos valores (assumindo que sejam valores e não variáveis).
dallin,
3

Você pode encontrar o objeto na matriz com a biblioteca Alasql :

var data = [ { name : "bob" , dinner : "pizza" }, { name : "john" , dinner : "sushi" },
     { name : "larry", dinner : "hummus" } ];

var res = alasql('SELECT * FROM ? WHERE dinner="sushi"',[data]);

Experimente este exemplo em jsFiddle .

agershun
fonte
3
Não tenho certeza se isso realmente merecia ser rejeitado. As consultas do tipo SQL em coleções tornam-se muito mais fáceis de raciocinar e escrever quando os requisitos se tornam mais complicados do que um único filtro ou chamada de redução. Alasql é uma biblioteca bastante impressionante, mas reconhecidamente um pouco exagerada para o exemplo acima.
TomDotTom
1
se o próprio objeto se originou de uma fonte SQL, então esta resposta é pura genialidade.
edwardsmarkf
1

Você pode usar um loop for simples:

for (prop in Obj){
    if (Obj[prop]['dinner'] === 'sushi'){

        // Do stuff with found object. E.g. put it into an array:
        arrFoo.push(Obj[prop]);
    }
}

O exemplo de violino a seguir coloca todos os objetos que contêm dinner:sushiem uma matriz:

https://jsfiddle.net/3asvkLn6/1/

Rotareti
fonte
1

Já existem muitas respostas boas aqui, então por que não mais uma, use uma biblioteca como lodash ou sublinhado :)

obj = {
   1 : { name : 'bob' , dinner : 'pizza' },
   2 : { name : 'john' , dinner : 'sushi' },
   3 : { name : 'larry', dinner : 'hummus' }
}

_.where(obj, {dinner: 'pizza'})
>> [{"name":"bob","dinner":"pizza"}]
TomDotTom
fonte
0

Tive que pesquisar uma estrutura de mapa de site aninhada para o primeiro item folha que maquina um determinado caminho. Eu vim com o código a seguir usando apenas .map() .filter()e .reduce. Retorna o último item encontrado que corresponda ao caminho /c.

var sitemap = {
  nodes: [
    {
      items: [{ path: "/a" }, { path: "/b" }]
    },
    {
      items: [{ path: "/c" }, { path: "/d" }]
    },
    {
      items: [{ path: "/c" }, { path: "/d" }]
    }
  ]
};

const item = sitemap.nodes
  .map(n => n.items.filter(i => i.path === "/c"))
  .reduce((last, now) => last.concat(now))
  .reduce((last, now) => now);

Editar 4n4904z07

Marc
fonte
0

Eu tentaria não reinventar a roda. Usamos a varredura de objetos para todas as nossas necessidades de processamento de dados. É conceitualmente muito simples, mas permite muitas coisas legais. Aqui está como você resolveria sua questão específica

Definição de Dados

const data = {
   1 : { name : 'bob' , dinner : 'pizza' },
   2 : { name : 'john' , dinner : 'sushi' },
   3 : { name : 'larry', dinner : 'hummus' }
};

Lógica

const objectScan = require('object-scan');

const scanner = (input) => {
  let obj = null;
  objectScan(['*.dinner'], {
    filterFn: (key, value, { parents }) => {
      if (value === 'sushi') {
        obj = parents[0];
      }
    },
    breakFn: () => obj !== null
  })(data);
  return obj;
};

const result = scanner(data);

Resultado

// => result
{
  "name": "john",
  "dinner": "sushi"
}
Vincent
fonte
0

Se você quiser encontrar um objeto específico por meio da função de pesquisa, tente algo assim:

    function findArray(value){

        let countLayer = dataLayer.length;
        for(var x = 0 ; x < countLayer ; x++){

            if(dataLayer[x].user){
                let newArr = dataLayer[x].user;
                let data = newArr[value];
                return data;
            }

        }

        return null;

    }

    findArray("id");

Este é um exemplo de objeto:

layerObj = {
    0: { gtm.start :1232542, event: "gtm.js"},
    1: { event: "gtm.dom", gtm.uniqueEventId: 52},
    2: { visitor id: "abcdef2345"},
    3: { user: { id: "29857239", verified: "Null", user_profile: "Personal", billing_subscription: "True", partners_user: "adobe"}
}

O código irá iterar e encontrar o array "usuário" e procurar o objeto que você procura dentro.

Meu problema era quando o índice da matriz mudava a cada atualização da janela e estava na terceira ou na segunda matriz, mas isso não importa.

Caiu como uma luva para mim!

No seu exemplo, é um pouco mais curto:

function findArray(value){

    let countLayer = Object.length;
    for(var x = 0 ; x < countLayer ; x++){

        if(Object[x].dinner === value){
            return Object[x];
        }

    }

    return null;

}

findArray('sushi');
z3r0
fonte