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")

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

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 */
}

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.


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


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
}

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


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.


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


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

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.


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.


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


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

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.


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

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.


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.");
}

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');
    }
}

0

Como alternativa, você pode fazer:

const find = (key, needle) => return !!~vendors.findIndex(v => (v[key] === needle));

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})


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) {
   ...
}

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

-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))

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
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.