Come determinare se l'array Javascript contiene un oggetto con un attributo uguale a un determinato valore?


658

Ho un array come

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

Come posso controllare questo array per vedere se Magenic esiste? Non voglio fare il giro, a meno che non sia necessario. Sto lavorando con potenzialmente un paio di migliaia di dischi.


AGGIORNATO

Dal momento che questo è stato un post popolare, ho pensato di condividere qualcosa di nuovo che ho trovato. E sembra che @CAFxX abbia già condiviso questo! Dovrei leggerli più spesso. Mi sono imbattuto in https://benfrain.com/understanding-native-javascript-array-methods/ .

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

E con ECMAScript 2015 è ancora più semplice utilizzare le nuove funzioni freccia:

vendors.filter(vendor => vendor.Name === "Magenic")

Per favore, scusa il commento apparentemente casuale, ma la tua domanda riguardava JSON o solo array JavaScript?
Alex Turpin,

4
La soluzione @CAFxX è migliore, sarebbe eccezionale se aggiorni la soluzione selezionata.
eMarine,

1
D'accordo, non l'ho visto prima!
David Lozzi,

Ora puoi semplificare ancora di più usando le funzioni freccia. Tutti i browser moderni supportano questo aspetto e sembrano più belli.
Piotr Kula,

puoi usare la funzione mappa, molto utile
Monir alhussini,

Risposte:


264

Modifica del 2018 : questa risposta è del 2011, prima che i browser avessero ampiamente supportato i metodi di filtraggio dell'array e le funzioni delle frecce. Dai un'occhiata alla risposta di CAFxX .

Non esiste un modo "magico" per cercare qualcosa in un array senza loop. Anche se si utilizza una funzione, la funzione stessa utilizzerà un ciclo. Quello che puoi fare è uscire dal ciclo non appena trovi quello che stai cercando per ridurre al minimo il tempo di calcolo.

var found = false;
for(var i = 0; i < vendors.length; i++) {
    if (vendors[i].Name == 'Magenic') {
        found = true;
        break;
    }
}

4
Nessun problema. Tieni presente che la soluzione di Keith è anche molto praticabile e ti salva dal looping.
Alex Turpin,

2
Non hai bisogno di una bandiera se tutto ciò che devi sapere è se "qualcosa" è dentro o no, puoi semplicemente controllare il valore dell'indice di scansione con la dimensione dell'array. Perché ciò funzioni l'indice var deve essere dichiarato prima dell'istruzione for ovviamente.
Alex,

5
Queste opzioni sembrano funzionare ora: vendors.forEach, vendors.filter, vendors.reduce
David Lozzi

1
che dire di JSON.stringify (venditori) .indexOf ('Magenic')! == -1
Last Breath

1
@LastBreath che potrebbe risultare abbastanza facilmente in un falso positivo se si 'Magenic'trova da qualche altra parte nell'oggetto
Alex Turpin

949

Non c'è bisogno di reinventare il ruotaloop, almeno non esplicitamente (utilizzando le funzioni freccia , solo i browser moderni ):

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

o meglio ancora :

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

MODIFICA: Se hai bisogno di compatibilità con browser scadenti, la soluzione migliore è:

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

4
@Rocket perché hai modificato la mia risposta? La sintassi senza parentesi graffe è javascript perfettamente valido .
CAFxX,

4
La sintassi "lambda" non funziona ancora in Chrome 16 (che non è un browser scadente).
Rocket Hazmat,

27
Dipende dalla tua definizione di pessimo, immagino. Tale sintassi fa parte di javascript 1.8.
CAFxX,

7
Le chiusure di espressioni che stai usando qui nel primo e nel secondo esempio hanno un Non standard, non usare! avvertimento da Mozilla (vedi quel link). Hanno sempre funzionato su Firefox e ora sono deprecati e verranno rimossi a favore delle funzioni freccia .
doppelgreener,

2
@ 7hibault perché somepuò cortocircuitare una volta name === "Magenic"trovato un oggetto con . Con filter, controllerà ogni elemento fino alla fine dell'array e creerà un nuovo oggetto array che corrisponde alla condizione, quindi controllalength
adiga

93

Nessun loop necessario. Tre metodi che mi vengono in mente:

Array.prototype.some ()

Questa è la risposta più esatta alla tua domanda, ovvero "controlla se esiste qualcosa", implicando un risultato bool. Questo sarà vero se ci sono oggetti 'Magenic', falso altrimenti:

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

Array.prototype.filter ()

Questo restituirà un array di tutti gli oggetti 'Magenic', anche se ce n'è solo uno (restituirà un array a un elemento):

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

Se si tenta di forzare questo su un valore booleano, non funzionerà, poiché un array vuoto (nessun oggetto "Magenico") è ancora vero. Quindi basta usare magenicVendors.lengthnel tuo condizionale.

Array.prototype.find ()

Questo restituirà il primo oggetto 'Magenic' (o undefinedse non ce ne sono):

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

Questo porta a un ok booleano (qualsiasi oggetto è vero, undefinedè falso).


Nota: sto usando il fornitore ["Nome"] invece di vendor.Name a causa del bizzarro involucro dei nomi delle proprietà.

Nota 2: Nessun motivo per utilizzare l'uguaglianza libera (==) invece dell'uguaglianza rigorosa (===) quando si controlla il nome.


5
È utile sottolineare che sotto il cofano, questi sono tutti in loop. Questi sono anche tutti più lenti dal punto di vista computazionale rispetto al semplice ciclo e all'esecuzione di operazioni.
ThePartyTurtle il

Potresti anche condividere questo amore qui: stackoverflow.com/questions/21748670/…, quindi più persone come me non accedono a quella vecchia pagina e fanno ipotesi.
ThePartyTurtle,

43

La risposta accettata funziona ancora, ma ora abbiamo un metodo nativo ECMAScript 6 [Array.find][1]per ottenere lo stesso effetto.

Citando MDN:

Il metodo find () restituisce il valore del primo elemento dell'array che soddisfa la funzione di test fornita. In caso contrario viene restituito undefined.

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
}

Vedi il mio link jsfiddle Esiste un polyfill per IE fornito da Mozilla


2
Potrebbe essere più breve se lo fai return ele.id == '2', ma +1 per una buona soluzione ES6.
Lye Fish

È bello avere una nuova risposta :) Mi chiedo solo se le prestazioni sono migliori o meno delle risposte sopra ...
Emidomenge

Penso che sia importante sottolineare che il valore di ritorno di 'data' (quando ele.id corrisponde a un id, come '21') sarà l'oggetto stesso dell'array (in questo caso, l'intero oggetto oggetto). Se l'aspettativa era che il risultato della variabile di dati fosse "vero" o "falso" anziché un valore di falsa, rimarresti profondamente deluso.
adamgede,

Grazie! Il mio compito era un po 'diverso. Ottieni l'indice di Object in the Array => push if <0 || splice(index, 1)ecco il mio codice un po 'aggiornato:const index = this.selected.indexOf(this.selected.find(s => s.id == passedObj.id))
Leonid Zadorozhnykh

30

Ecco come lo farei

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

array.some()Il metodo controlla se esiste almeno un valore in un array che corrisponde ai criteri e restituisce un valore booleano. Da qui in poi puoi andare con:

if (found) {
// do something
} else {
// do something else
}

22

A meno che tu non voglia ristrutturarlo in questo modo:

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

a cui puoi fare if(vendors.Magnetic)

Dovrai eseguire il loop


2
Nel caso in cui volesse ancora mantenere la struttura dell'oggetto per usarla altrove
Keith.Abramo

21

Come da specifica ECMAScript 6, è possibile utilizzare findIndex.

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

magenicIndexconterrà 0(che è l'indice nella matrice) o -1se non è stato trovato.


Solo per rendere le persone consapevoli del fatto che 0 corrisponderebbe comunque come risultato falso se fosse usato come condizione. Per questo motivo penso che find () sia migliore quando ottieni una valutazione di verità più logica .
dhj

15

Poiché l'OP ha posto la domanda se la chiave esiste o meno .

Può essere una soluzione più elegante che restituirà valore booleano utilizzando la funzione di riduzione ES6

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

Nota: il parametro iniziale di riduci è a falsee se l'array ha la chiave tornerà vero.

Spero che sia d'aiuto per un'implementazione del codice migliore e più pulita


1
Da quando !! [] è uguale a falso?
Sergey

1
Bella presa. Risposta aggiornata usando ridurre :)
Jay Chakra

1
Questo è sbagliato. Il primo parametro a reduceè l'accumulatore e non l' vendoroggetto. Questo controlla false.Name === "Magenic"in ogni ciclo e restituisce false
adiga il

@adiga: corretto.
Jay Chakra

1
Controlla anche la soluzione di Mirza Leka. Una soluzione molto più elegante.
Jay Chakra

13

Non puoi senza davvero guardare l'oggetto.

Probabilmente dovresti cambiare un po 'la tua struttura, come

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

Quindi puoi semplicemente usarlo come un hash di ricerca.

vendors['Microsoft']; // 'DEF'
vendors['Apple']; // undefined

6

Potrebbe essere troppo tardi, ma l'array javascript ha due metodi someeevery metodo che restituisce un valore booleano e possono aiutarti a raggiungere questo obiettivo.

Penso che somesarebbe più appropriato per quello che intendi raggiungere.

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

Alcuni confermano che uno qualsiasi degli oggetti nell'array soddisfa la condizione data.

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

Ogni convalida che tutti gli oggetti nell'array soddisfino la condizione data.


Non funziona const array1 = [{name:'Mike'},{name:'Alice'}]; console.log(array1.every(item => item.name !== 'Mike'));, dovrebbe tornare vero
Thanwa Ch.

Mi spiace amico, intendevo some, aggiornerò la mia risposta.
Akinjiola Toni,

5

Devi fare un giro, non c'è modo di aggirarlo.

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

Ovviamente potresti usare una libreria come linq.js per renderlo più piacevole:

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

(vedi jsFiddle per una demo)

Dubito che linq.js sarà più veloce di un ciclo diretto, ma è certamente più flessibile quando le cose diventano un po 'più complicate.


5

Test per gli elementi dell'array:

JS offre funzioni di array che consentono di raggiungerlo in modo relativamente semplice. Sono i seguenti:

  1. Array.prototype.filter: Accetta una funzione di callback che è un test, l'array viene quindi ripetuto con il callback e filtrato in base a questo callback. Viene restituito un nuovo array filtrato.
  2. Array.prototype.some: Accetta una funzione di callback che è un test, l'array viene quindi ripetuto con il callback e se un elemento supera il test, viene restituito il valore booleano true. Altrimenti viene restituito falso

Le specifiche sono spiegate meglio con un esempio:

Esempio:

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

Supporto per il browser:

Queste 2 funzioni sono ES6funzioni, non tutti i browser potrebbero supportarle. Per ovviare a questo è possibile utilizzare un polyfill. Ecco il polyfill per Array.prototype.some(da 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 stai usando jquery puoi sfruttare grep per creare array con tutti gli oggetti corrispondenti:

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

e quindi utilizzare l'array dei risultati:

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

3

Correggimi se sbaglio .. avrei potuto usare un forEachmetodo come questo,

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

   }
});

Oggi ci sono abituato, per la sua semplicità e la sua parola esplicativa. Grazie.


1
Nota: nessun uso di ritorno qui
Edison,

2

Puoi provare questo suo lavoro per me.

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

Bene, questa è una domanda davvero vecchia e penso che il suo aggiornamento abbia già la soluzione migliore al giorno d'oggi.
Federico Galfione,

1

Puoi usare lodash . Se la libreria lodash è troppo pesante per la tua applicazione, considera di non utilizzare una funzione superflua.

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

Questo è solo un modo per farlo. Un altro può essere:

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

console.log(arr);

L'esempio sopra può anche essere riscritto senza usare librerie come:

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

Spero che la mia risposta aiuti.


1

Molte risposte qui sono buone e abbastanza facili. Ma se la tua matrice di oggetti ha un set fisso di valori, puoi usare il trucco seguente:

Mappa tutto il nome in un oggetto.

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

Ora questo dirtyObj è possibile utilizzarlo ancora e ancora senza alcun ciclo.

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

1

Per confrontare un oggetto con un altro, combino un ciclo for in (usato per scorrere gli oggetti) e some (). Non devi preoccuparti di un array che va oltre i limiti, ecc., In questo modo si risparmia del codice. La documentazione su .some è disponibile qui

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

Un modo alternativo per confrontare un oggetto con un altro è usare un ciclo nidificato per Object.keys (). Length per ottenere la quantità di oggetti nell'array. Codice sotto:

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

Per rispondere alla tua domanda esatta, se stai solo cercando un valore in un oggetto, puoi usare un singolo ciclo in.

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

In alternativa puoi fare:

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

1
faresti meglio a dire perché può farlo
Azzabi Haythem

0

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

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

Risultato: senza2 ([{id: 1}, {id: 1}, {id: 2}], {id: 2})


Penso che volevi dire Risultato: [{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
Si prega di fornire una descrizione e assicurarsi che l'esempio fornito funzioni (il filtro non modificherà l'array originale ma lo clonerà).
Moshe Simantov,

-1

Il mio approccio per risolvere questo problema è usare ES6 e creare una funzione che faccia il controllo per noi. Il vantaggio di questa funzione è che può essere riutilizzabile in tutto il progetto per controllare qualsiasi array di oggetti dati keye valueda controllare.

Abbastanza parlare, vediamo il codice

Vettore

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

Funzione

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

Chiamata / Uso

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

const found = arrayIncludesInObj(ceos, "name", "Tim Bezos"); // false

-4

Preferirei andare con regex.

Se il tuo codice è il seguente,

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

io raccomanderei

/"Name":"Magenic"/.test(JSON.stringify(vendors))

24
Alcune persone, di fronte a un problema, pensano "Lo so, userò espressioni regolari". Ora hanno due problemi.
Craicerjack,

File questo sotto, solo perché puoi fare qualcosa, non significa che dovresti.
Liam,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.