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.