Como determinar se a matriz Javascript contém um objeto com um atributo igual a um determinado valor?

658

Eu tenho uma matriz como

vendors = [
    {
      Name: 'Magenic',
      ID: 'ABC'
     },
    {
      Name: 'Microsoft',
      ID: 'DEF'
    } //and so on goes array... 
];

Como verifico esta matriz para ver se existe Magenic? Não quero fazer um loop, a menos que precise. Estou trabalhando com potencialmente alguns milhares de discos.


ATUALIZADA

Como esse post foi popular, pensei em compartilhar algo novo que encontrei. E parece que o @CAFxX já compartilhou isso! Eu deveria ler isso com mais frequência. Me deparei com https://benfrain.com/understanding-native-javascript-array-methods/ .

vendors.filter(function(vendor){ return vendor.Name === "Magenic" })

E com o ECMAScript 2015 , é ainda mais simples usar as novas funções de seta:

vendors.filter(vendor => vendor.Name === "Magenic")
David Lozzi
fonte
Perdoe o comentário aparentemente aleatório, mas sua pergunta dizia respeito ao JSON ou apenas a matrizes JavaScript?
precisa
4
A solução @CAFxX é melhor, seria incrível se você atualizar a solução selecionada.
EMarine
1
Concordou, não vi isso antes!
precisa saber é o seguinte
Você pode simplificar isso agora ainda mais usando as funções de seta. Todos os navegadores modernos suportam isso e parecem mais agradáveis.
Piotr Kula
você pode usar a função de mapa, muito útil
Monir alhussini

Respostas:

264

Edição de 2018 : esta resposta é de 2011, antes que os navegadores tivessem amplo suporte a métodos de filtragem de matriz e funções de seta. Ter um olhar para a resposta de CAFxX .

Não existe uma maneira "mágica" de verificar algo em uma matriz sem um loop. Mesmo se você usar alguma função, a própria função usará um loop. O que você pode fazer é sair do circuito assim que encontrar o que procura para minimizar o tempo computacional.

var found = false;
for(var i = 0; i < vendors.length; i++) {
    if (vendors[i].Name == 'Magenic') {
        found = true;
        break;
    }
}
Alex Turpin
fonte
4
Sem problemas. Lembre-se de que a solução do Keith também é muito viável e evita que você faça loop.
precisa
2
Você não precisa de um sinalizador se tudo o que você precisa saber é se "algo" está ou não, basta verificar o valor do índice de verificação com o tamanho da matriz. Para que isso funcione, o índice var precisa ser declarado antes da instrução for, é claro.
31414 Alex
5
Estas opções parecem funcionar agora: vendors.forEach, vendors.filter, vendors.reduce
David Lozzi
1
que tal JSON.stringify (vendors) .indexOf ('Magenic')! == -1
Última respiração
1
@LastBreath que poderia resultar em um falso positivo muito facilmente se 'Magenic'está em outro lugar no objeto
Alex Turpin
948

Não há necessidade de reinventar o rodaloop, pelo menos não explicitamente (usando funções de seta , apenas navegadores modernos ):

if (vendors.filter(e => e.Name === 'Magenic').length > 0) {
  /* vendors contains the element we're looking for */
}

ou, melhor ainda :

if (vendors.some(e => e.Name === 'Magenic')) {
  /* vendors contains the element we're looking for */
}

EDIT: Se você precisa de compatibilidade com navegadores ruins, sua melhor aposta é:

if (vendors.filter(function(e) { return e.Name === 'Magenic'; }).length > 0) {
  /* vendors contains the element we're looking for */
}
CAFxX
fonte
4
@Rocket Por que você editou minha resposta? A sintaxe sem os chavetas é perfeitamente válida em javascript .
CAFxX
4
A sintaxe "lambda" ainda não funciona no Chrome 16 (que não é um navegador ruim).
Foguete Hazmat
27
Depende da sua definição de péssimo, eu acho. Essa sintaxe faz parte do javascript 1.8.
CAFxX 22/11/11
7
A expressão closures que você está usando aqui no primeiro e no segundo exemplos tem um Non-standard, não use! aviso do Mozilla (veja o link). Eles só funcionaram no Firefox e agora estão obsoletos e serão removidos em favor das funções de seta .
Doppelgreener 07/12/16
2
@ 7hibault porque somepode causar um curto-circuito quando um objeto name === "Magenic"é encontrado. Com filter, ele irá verificar cada item até o final da matriz e criar um novo itens de matriz que correspondem a condição, então verificar olength
Adiga
93

Nenhum loop é necessário. Três métodos que vêm à mente:

Array.prototype.some ()

Esta é a resposta mais exata para sua pergunta, ou seja, "verifique se existe alguma coisa", implicando um resultado booleano. Isso será verdadeiro se houver algum objeto 'Magenic', caso contrário, false:

let hasMagenicVendor = vendors.some( vendor => vendor['Name'] === 'Magenic' )

Array.prototype.filter ()

Isso retornará uma matriz de todos os objetos 'Magenic', mesmo se houver apenas uma (retornará uma matriz de um elemento):

let magenicVendors = vendors.filter( vendor => vendor['Name'] === 'Magenic' )

Se você tentar coagir isso a um booleano, ele não funcionará, pois uma matriz vazia (sem objetos 'Magenic') ainda é verdadeira. Então, basta usar magenicVendors.lengthem seu condicional.

Array.prototype.find ()

Isso retornará o primeiro objeto 'Magenic' (ou, undefinedse não houver):

let magenicVendor = vendors.find( vendor => vendor['Name'] === 'Magenic' );

Isso obriga a um valor booleano (qualquer objeto é verdadeiro, undefinedé falso).


Nota: Estou usando o fornecedor ["Nome"] em vez de vendor.Name devido ao invólucro estranho dos nomes das propriedades.

Nota 2: Não há razão para usar igualdade solta (==) em vez de igualdade estrita (===) ao verificar o nome.

boxtrain
fonte
5
É útil ressaltar que, sob o capô, todos estão em loop. Também são todos mais lentos em termos computacionais do que simplesmente para executar loop e executar operações.
ThePartyTurtle 18/01
Também pode compartilhar esse amor aqui: stackoverflow.com/questions/21748670/…, para que mais pessoas como eu não naveguem para a página antiga e façam suposições.
precisa saber é o seguinte
43

A resposta aceita ainda funciona, mas agora temos um método nativo ECMAScript 6 [Array.find][1]para obter o mesmo efeito.

Citando MDN:

O método find () retorna o valor do primeiro elemento na matriz que satisfaz a função de teste fornecida. Caso contrário, indefinido é retornado.

var arr = []; 
var item = {
  id: '21',
  step: 'step2',
  label: 'Banana',
  price: '19$'
};

arr.push(item);
/* note : data is the actual object that matched search criteria 
  or undefined if nothing matched */
var data = arr.find( function( ele ) { 
    return ele.id === '21';
} );

if( data ) {
 console.log( 'found' );
 console.log(data); // This is entire object i.e. `item` not boolean
}

Veja meu link do jsfiddle Há um polyfill para IE fornecido pelo mozilla

TeaCoder
fonte
2
Poderia ser mais curto se você apenas o fizer return ele.id == '2', mas +1 para uma boa solução ES6.
quer
É bom ter resposta fresco :) Basta saber se o desempenho é melhor ou não do que respostas acima ...
Emidomenge
Eu acho importante ressaltar que o valor de retorno de 'dados' (quando ele.id corresponder a um ID, como '21') será o próprio item da matriz (nesse caso, todo o objeto do item). Se a expectativa era de que o resultado da variável de dados fosse 'verdadeiro' ou 'falso' em vez de um valor falso, você ficaria muito decepcionado.
adamgede
Valeu! Minha tarefa foi um pouco diferente. Obtenha o índice de Objeto na matriz => push if <0 || splice(index, 1)aqui está o meu código um pouco atualizado: #const index = this.selected.indexOf(this.selected.find(s => s.id == passedObj.id))
Leonid Zadorozhnykh
30

Aqui está o jeito que eu faria

const found = vendors.some(item => item.Name === 'Magenic');

array.some()O método verifica se há pelo menos um valor em uma matriz que corresponde aos critérios e retorna um booleano. A partir daqui, você pode ir com:

if (found) {
// do something
} else {
// do something else
}
Mirza Leka
fonte
22

A menos que você queira reestruturá-lo assim:

vendors = {
    Magenic: {
      Name: 'Magenic',
      ID: 'ABC'
     },
    Microsoft: {
      Name: 'Microsoft',
      ID: 'DEF'
    } and so on... 
};

para o qual você pode fazer if(vendors.Magnetic)

Você terá que fazer um loop

Keith.Abramo
fonte
2
No caso, ele ainda queria manter a estrutura do objeto para usá-lo mais onde
Keith.Abramo
21

De acordo com a especificação ECMAScript 6, você pode usar findIndex.

const magenicIndex = vendors.findIndex(vendor => vendor.Name === 'Magenic');

magenicIndexreterá 0(que é o índice na matriz) ou -1se não foi encontrado.

Lucas Bento
fonte
Apenas para conscientizar as pessoas de que 0 ainda corresponderia a um resultado falso se isso fosse usado como condição. Por esse motivo, acho que find () é melhor à medida que você obtém uma avaliação mais lógica da verdade.
dhj
15

Como o OP fez a pergunta se a chave existe ou não .

Uma solução mais elegante que retornará booleano usando a função de redução ES6 pode ser

const magenicVendorExists =  vendors.reduce((accumulator, vendor) => (accumulator||vendor.Name === "Magenic"), false);

Nota: O parâmetro inicial de reduzir é ae, falsese a matriz tiver a chave, ela retornará verdadeiro.

Espero que ajude a uma implementação melhor e mais limpa do código

Jay Chakra
fonte
1
Desde quando !! [] é igual a falso?
Sergey
1
Boa pegada. Resposta atualizada usando o reduzir :)
Jay Chakra
1
Isto está errado. O primeiro parâmetro para reduceé o acumulador e não o vendorobjeto. Isso verifica false.Name === "Magenic"todos os circuitos e retorna falso
adiga 18/06/19
@adiga: corrigido.
Jay Chakra
1
Verifique também a solução de Mirza Leka. Uma solução muito mais elegante.
Jay Chakra
13

Você não pode sem olhar realmente para o objeto.

Você provavelmente deve mudar um pouco sua estrutura, como

vendors = {
    Magenic:   'ABC',
    Microsoft: 'DEF'
};

Então você pode usá-lo como um hash de pesquisa.

vendors['Microsoft']; // 'DEF'
vendors['Apple']; // undefined
jAndy
fonte
6

Pode ser tarde demais, mas a matriz javascript possui dois métodos somee o everymétodo que retorna um booleano e pode ajudá-lo a conseguir isso.

Eu acho someque seria mais apropriado para o que você pretende alcançar.

vendors.some( vendor => vendor['Name'] !== 'Magenic' )

Alguns valida que qualquer um dos objetos na matriz satisfaça a condição especificada.

vendors.every( vendor => vendor['Name'] !== 'Magenic' )

Every valida que todos os objetos na matriz satisfazem a condição especificada.

Akinjiola Toni
fonte
Não funciona const array1 = [{name:'Mike'},{name:'Alice'}]; console.log(array1.every(item => item.name !== 'Mike'));, deve retornar verdadeiro
Thanwa Ch.
Desculpe amigo, eu quis dizer some, irá atualizar minha resposta.
Akinjiola Toni 30/03
5

Você tem que fazer um loop, não há como contornar isso.

function seekVendor(vendors, name) {
  for (var i=0, l=vendors.length; i<l; i++) {
    if (typeof vendors[i] == "object" && vendors[i].Name === name) {
      return vendors[i];
    }
  }
}

Claro que você pode usar uma biblioteca como linq.js para tornar isso mais agradável:

Enumerable.From(vendors).Where("$.Name == 'Magenic'").First();

(veja jsFiddle para uma demonstração)

Duvido que o linq.js seja mais rápido que um loop direto, mas certamente é mais flexível quando as coisas ficam um pouco mais complicadas.

Tomalak
fonte
5

Testando elementos de matriz:

JS Oferece funções de matriz que permitem que você consiga isso com relativa facilidade. Eles são os seguintes:

  1. Array.prototype.filter: Executa uma função de retorno de chamada que é um teste; a matriz é iterada com o retorno de chamada e filtrada de acordo com esse retorno de chamada. Uma nova matriz filtrada é retornada.
  2. Array.prototype.some: Executa uma função de retorno de chamada que é um teste, a matriz é iterada com o retorno de chamada e, se algum elemento passa no teste, o valor booleano verdadeiro é retornado. Caso contrário, false será retornado

As especificidades são melhor explicadas através de um exemplo:

Exemplo:

vendors = [
    {
      Name: 'Magenic',
      ID: 'ABC'
     },
    {
      Name: 'Microsoft',
      ID: 'DEF'
    } //and so on goes array... 
];

// filter returns a new array, we instantly check if the length 
// is longer than zero of this newly created array
if (vendors.filter(company => company.Name === 'Magenic').length ) {
  console.log('I contain Magenic');
}

// some would be a better option then filter since it directly returns a boolean
if (vendors.some(company => company.Name === 'Magenic')) {
  console.log('I also contain Magenic');
}

Suporte do navegador:

Essas duas funções são ES6funcionais, nem todos os navegadores podem suportá-los. Para superar isso, você pode usar um polyfill. Aqui está o polyfill para Array.prototype.some(do MDN):

if (!Array.prototype.some) {
  Array.prototype.some = function(fun, thisArg) {
    'use strict';

    if (this == null) {
      throw new TypeError('Array.prototype.some called on null or undefined');
    }

    if (typeof fun !== 'function') {
      throw new TypeError();
    }

    var t = Object(this);
    var len = t.length >>> 0;

    for (var i = 0; i < len; i++) {
      if (i in t && fun.call(thisArg, t[i], i, t)) {
        return true;
      }
    }

    return false;
  };
}

Willem van der Veen
fonte
4

se você estiver usando jquery, poderá aproveitar o grep para criar um array com todos os objetos correspondentes:

var results = $.grep(vendors, function (e) {
    return e.Name == "Magenic";
});

e use a matriz de resultados:

for (var i=0, l=results.length; i<l; i++) {
    console.log(results[i].ID);
}
Eitan
fonte
3

Corrija-me se estiver errado .. eu poderia ter usado um forEachmétodo como este,

var found=false;
vendors.forEach(function(item){
   if(item.name === "name"){
       found=true;

   }
});

Hoje em dia estou acostumado, por causa da simplicidade e da palavra autoexplicativa. Obrigado.

Siddhesh Mishra
fonte
1
Nota: nenhum uso de retorno aqui
Edison
2

Você pode tentar este trabalho para mim.

const _ = require('lodash');

var arr = [
  {
    name: 'Jack',
    id: 1
  },
  {
    name: 'Gabriel',
    id: 2
  },
  {
    name: 'John',
    id: 3
  }
]

function findValue(arr,value) {
  return _.filter(arr, function (object) {
    return object['name'].toLowerCase().indexOf(value.toLowerCase()) >= 0;
  });
}

console.log(findValue(arr,'jack'))
//[ { name: 'Jack', id: 1 } ]
jenish
fonte
Bem, essa é uma pergunta muito antiga e acho que sua atualização já tem a melhor solução hoje em dia.
Federico Galfione
1

Você pode usar lodash . Se a biblioteca lodash for muito pesada para o seu aplicativo, considere separar funções desnecessárias não usadas.

let newArray = filter(_this.props.ArrayOne, function(item) {
                    return find(_this.props.ArrayTwo, {"speciesId": item.speciesId});
                });

Esta é apenas uma maneira de fazer isso. Outro pode ser:

var newArray=  [];
     _.filter(ArrayOne, function(item) {
                        return AllSpecies.forEach(function(cItem){
                            if (cItem.speciesId == item.speciesId){
                            newArray.push(item);
                          }
                        }) 
                    });

console.log(arr);

O exemplo acima também pode ser reescrito sem o uso de bibliotecas como:

var newArray=  [];
ArrayOne.filter(function(item) {
                return ArrayTwo.forEach(function(cItem){
                    if (cItem.speciesId == item.speciesId){
                    newArray.push(item);
                  }
                }) 
            });
console.log(arr);

Espero que minha resposta ajude.

Abhay Shiro
fonte
1

Muitas respostas aqui são boas e muito fáceis. Mas se sua matriz de objetos estiver com um conjunto fixo de valores, você poderá usar o truque abaixo:

Mapeie todo o nome em um objeto.

vendors = [
    {
      Name: 'Magenic',
      ID: 'ABC'
     },
    {
      Name: 'Microsoft',
      ID: 'DEF'
    }
];

var dirtyObj = {}
for(var count=0;count<vendors.length;count++){
   dirtyObj[vendors[count].Name] = true //or assign which gives you true.
}

Agora esse dirtyObj você pode usar repetidamente sem nenhum loop.

if(dirtyObj[vendor.Name]){
  console.log("Hey! I am available.");
}
jesusverma
fonte
1

Para comparar um objeto com outro, eu combino um loop for in (usado para percorrer objetos) e some (). Você não precisa se preocupar com uma matriz fora dos limites, etc, para que economize algum código. A documentação sobre .some pode ser encontrada aqui

var productList = [{id: 'text3'}, {id: 'text2'}, {id: 'text4', product: 'Shampoo'}]; // Example of selected products
var theDatabaseList = [{id: 'text1'}, {id: 'text2'},{id: 'text3'},{id:'text4', product: 'shampoo'}];    
var  objectsFound = [];

for(let objectNumber in productList){
    var currentId = productList[objectNumber].id;   
    if (theDatabaseList.some(obj => obj.id === currentId)) {
        // Do what you need to do with the matching value here
        objectsFound.push(currentId);
    }
}
console.log(objectsFound);

Uma maneira alternativa de comparar um objeto a outro é usar um loop for aninhado com Object.keys (). Length para obter a quantidade de objetos na matriz. Código abaixo:

var productList = [{id: 'text3'}, {id: 'text2'}, {id: 'text4', product: 'Shampoo'}]; // Example of selected products
var theDatabaseList = [{id: 'text1'}, {id: 'text2'},{id: 'text3'},{id:'text4', product: 'shampoo'}];    
var objectsFound = [];

for(var i = 0; i < Object.keys(productList).length; i++){
        for(var j = 0; j < Object.keys(theDatabaseList).length; j++){
        if(productList[i].id === theDatabaseList[j].id){
            objectsFound.push(productList[i].id);
        }       
    }
}
console.log(objectsFound);

Para responder sua pergunta exata, se você estiver apenas procurando por um valor em um objeto, use um loop for in único.

var vendors = [
    {
      Name: 'Magenic',
      ID: 'ABC'
     },
    {
      Name: 'Microsoft',
      ID: 'DEF'
    } 
];

for(var ojectNumbers in vendors){
    if(vendors[ojectNumbers].Name === 'Magenic'){
        console.log('object contains Magenic');
    }
}
Spangle
fonte
0

Como alternativa, você pode fazer:

const find = (key, needle) => return !!~vendors.findIndex(v => (v[key] === needle));
BrunoWest
fonte
1
é melhor você dizer por que ele pode fazer isso
Azzabi Haythem
0

var without2 = (arr, args) => arr.filter(v => v.id !== args.id); Exemplo:

without2([{id:1},{id:1},{id:2}],{id:2})

Resultado: sem2 ([{id: 1}, {id: 1}, {id: 2}], {id: 2})

behzad abbasi
fonte
Eu acho que você quis dizer Resultado: [{id: 1}, {id: 1}] #
Isaac Pak
0
const a = [{one:2},{two:2},{two:4}]
const b = a.filter(val => "two" in val).length;
if (b) {
   ...
}
user1665355
fonte
3
Por favor, e alguma descrição e verifique se o exemplo que você fornece funciona .. (o filtro não altera a matriz original, mas a clona).
Moshe Simantov
-1

Minha abordagem para resolver esse problema é usar o ES6 e criar uma função que faça a verificação para nós. O benefício dessa função é que pode ser reutilizável em todo o seu projeto para verificar qualquer matriz de objetos, dada a keye a valueverificação.

CONVERSA BASTANTE, VAMOS O CÓDIGO

Matriz

const ceos = [
  {
    name: "Jeff Bezos",
    company: "Amazon"
  }, 
  {
    name: "Mark Zuckerberg",
    company: "Facebook"
  }, 
  {
    name: "Tim Cook",
    company: "Apple"
  }
];

Função

const arrayIncludesInObj = (arr, key, valueToCheck) => {
  let found = false;

  arr.some(value => {
    if (value[key] === valueToCheck) {
      found = true;
      return true; // this will break the loop once found
    }
  });

  return found;
}

Chamada / Uso

const found = arrayIncludesInObj(ceos, "name", "Tim Cook"); // true

const found = arrayIncludesInObj(ceos, "name", "Tim Bezos"); // false
rotimi-best
fonte
-4

Eu prefiro ir com regex.

Se o seu código for o seguinte,

vendors = [
    {
      Name: 'Magenic',
      ID: 'ABC'
     },
    {
      Name: 'Microsoft',
      ID: 'DEF'
    }
];

eu recomendaria

/"Name":"Magenic"/.test(JSON.stringify(vendors))
sangwook kim
fonte
24
Algumas pessoas, quando confrontadas com um problema, pensam "eu sei, vou usar expressões regulares". Agora eles tem dois problemas.
Craicerjack
Arquive isso abaixo, apenas porque você pode fazer algo, não significa que você deveria.
Liam