Comment déterminer si un tableau Javascript contient un objet avec un attribut égal à une valeur donnée?

659

J'ai un tableau comme

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

Comment puis-je vérifier ce tableau pour voir si Magenic existe? Je ne veux pas faire de boucle, sauf si je le dois. Je travaille avec potentiellement quelques milliers de disques.


MISE À JOUR

Comme il s'agit d'un article populaire, j'ai pensé partager quelque chose de nouveau que j'ai trouvé. Et il semble que @CAFxX a déjà partagé cela! Je devrais les lire plus souvent. Je suis tombé sur https://benfrain.com/understanding-native-javascript-array-methods/ .

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

Et avec ECMAScript 2015, il est encore plus simple d'utiliser les nouvelles fonctions fléchées:

vendors.filter(vendor => vendor.Name === "Magenic")
David Lozzi
la source
Veuillez pardonner le commentaire apparemment aléatoire, mais votre question concernait-elle JSON ou simplement des tableaux JavaScript?
Alex Turpin
4
La solution @CAFxX est meilleure, ce serait génial si vous mettez à jour la solution sélectionnée.
eMarine
1
D'accord, je n'ai pas vu ça plus tôt!
David Lozzi
Vous pouvez simplifier encore plus cela en utilisant les fonctions fléchées. Tous les navigateurs modernes prennent en charge cela et semblent plus agréables.
Piotr Kula
vous pouvez utiliser la fonction de carte, très utile
Monir alhussini

Réponses:

264

Édition 2018 : cette réponse date de 2011, avant que les navigateurs ne disposent de méthodes de filtrage de tableau et de fonctions fléchées largement prises en charge. Jetez un œil à la réponse de CAFxX .

Il n'y a aucun moyen "magique" de vérifier quelque chose dans un tableau sans boucle. Même si vous utilisez une fonction, la fonction elle-même utilisera une boucle. Ce que vous pouvez faire, c'est sortir de la boucle dès que vous trouvez ce que vous cherchez pour minimiser le temps de calcul.

var found = false;
for(var i = 0; i < vendors.length; i++) {
    if (vendors[i].Name == 'Magenic') {
        found = true;
        break;
    }
}
Alex Turpin
la source
4
Aucun problème. Gardez à l'esprit que la solution de Keith est également très viable et vous évite de boucler.
Alex Turpin
2
Vous n'avez pas besoin d'un indicateur si tout ce que vous devez savoir est de savoir si "quelque chose" est dedans, vous pouvez simplement vérifier la valeur de l'index d'analyse avec la taille du tableau. Pour que cela fonctionne, l'index var doit être déclaré avant l'instruction for bien sûr.
Alex
5
Ces options semblent fonctionner maintenant: vendors.forEach, vendors.filter, vendors.reduce
David Lozzi
1
Qu'en est-il de JSON.stringify (vendeurs) .indexOf ('Magenic')! == -1
Last Breath
2
@LastBreath qui pourrait entraîner un faux positif assez facilement s'il se 'Magenic'trouve ailleurs dans l'objet
Alex Turpin
950

Pas besoin de réinventer le roueboucle, du moins pas explicitement (en utilisant les fonctions fléchées , les navigateurs modernes uniquement ):

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

ou mieux encore :

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

EDIT: Si vous avez besoin de compatibilité avec les navigateurs moche, alors votre meilleur pari est:

if (vendors.filter(function(e) { return e.Name === 'Magenic'; }).length > 0) {
  /* vendors contains the element we're looking for */
}
CAFxX
la source
4
@Rocket pourquoi avez-vous modifié ma réponse? La syntaxe sans les accolades est parfaitement javascript valide .
CAFxX
4
La syntaxe "lambda" ne fonctionne toujours pas dans Chrome 16 (qui n'est pas un navigateur pourri).
Rocket Hazmat
27
Cela dépend de votre définition de moche, je suppose. Cette syntaxe fait partie de javascript 1.8.
CAFxX
7
Les fermetures d'expression que vous utilisez ici dans les premier et deuxième exemples ont une valeur non standard, ne l'utilisez pas! avertissement de Mozilla (voir ce lien). Ils ne fonctionnaient que dans Firefox et sont désormais obsolètes et seront supprimés au profit des fonctions fléchées .
doppelgreener
2
@ 7hibault car somepeut court-circuiter une fois un objet name === "Magenic"trouvé. Avec filter, il vérifiera chaque élément jusqu'à la fin du tableau et créera un nouveau tableau correspondant à la condition, puis vérifier lelength
adiga
93

Aucune boucle nécessaire. Trois méthodes qui me viennent à l'esprit:

Array.prototype.some ()

C'est la réponse la plus exacte à votre question, c'est-à-dire "vérifier si quelque chose existe", impliquant un résultat booléen. Ce sera vrai s'il y a des objets «magéniques», faux sinon:

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

Array.prototype.filter ()

Cela retournera un tableau de tous les objets 'Magenic', même s'il n'y en a qu'un (retournera un tableau à un élément):

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

Si vous essayez de forcer cela à un booléen, cela ne fonctionnera pas, car un tableau vide (pas d'objets «magéniques») est toujours vrai. Il suffit donc de l'utiliser magenicVendors.lengthdans votre conditionnel.

Array.prototype.find ()

Cela retournera le premier objet 'Magenic' (ou undefineds'il n'y en a pas):

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

Cela aboutit à un accord booléen (tout objet est véridique, undefinedest faux).


Remarque: j'utilise le fournisseur ["Name"] au lieu de vendor.Name en raison de la casse étrange des noms de propriété.

Remarque 2: aucune raison d'utiliser l'égalité lâche (==) au lieu de l'égalité stricte (===) lors de la vérification du nom.

boxtrain
la source
5
Il est utile de souligner que sous le capot, ils sont tous en boucle. Celles-ci sont également toutes plus lentes sur le plan du calcul que simplement pour les opérations de bouclage et d'exécution.
ThePartyTurtle
Autant aller partager cet amour ici: stackoverflow.com/questions/21748670/… donc plus de gens comme moi ne naviguent pas vers cette ancienne page et ne font pas d'hypothèses.
ThePartyTurtle
43

La réponse acceptée fonctionne toujours mais nous avons maintenant une méthode native ECMAScript 6 [Array.find][1]pour obtenir le même effet.

Citant MDN:

La méthode find () renvoie la valeur du premier élément du tableau qui satisfait la fonction de test fournie. Sinon, undefined est renvoyé.

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
}

Voir mon lien jsfiddle Il y a un polyfill pour IE fourni par mozilla

TeaCoder
la source
2
Cela pourrait être plus court si vous le faites return ele.id == '2', mais +1 pour une bonne solution ES6.
Lye Fish
C'est bien d'avoir une nouvelle réponse :) Je me demande simplement si les performances sont meilleures ou non que les réponses ci-dessus ...
Emidomenge
Je pense qu'il est important de souligner que la valeur de retour de 'data' (lorsque ele.id correspond à un id, tel que '21') va être l'élément de tableau lui-même (dans ce cas, l'objet item entier). Si l'on s'attendait à ce que le résultat de la variable de données soit «vrai» ou «faux» au lieu d'une valeur fausse, vous seriez profondément déçu.
adamgede
THX! Ma tâche était un peu différente. Obtenez l'index de l'objet dans le tableau => push if <0 || splice(index, 1)voici mon code un peu mis à jour:const index = this.selected.indexOf(this.selected.find(s => s.id == passedObj.id))
Leonid Zadorozhnykh
30

Voici comment je le ferais

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

array.some()vérifie s'il existe au moins une valeur dans un tableau qui correspond aux critères et renvoie un booléen. A partir de là, vous pouvez aller avec:

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

A moins que vous ne vouliez le restructurer comme ceci:

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

auquel vous pouvez faire if(vendors.Magnetic)

Vous devrez boucler

Keith.Abramo
la source
2
Au cas où il voudrait toujours conserver la structure de l'objet pour l'utiliser ailleurs où
Keith.Abramo
21

Selon la spécification ECMAScript 6, vous pouvez utiliser findIndex.

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

magenicIndextiendra soit 0(qui est l'index dans le tableau) ou -1s'il n'a pas été trouvé.

Lucas Bento
la source
Juste pour faire comprendre aux gens que 0 correspondrait toujours à un faux résultat s'il était utilisé comme condition. Pour cette raison, je pense que find () est meilleur car vous obtenez une évaluation véridique plus logique .
dhj
15

Comme l'OP a posé la question de savoir si la clé existe ou non .

Une solution plus élégante qui retournera booléen en utilisant la fonction de réduction ES6 peut être

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

Remarque: Le paramètre initial de réduire est a falseet si le tableau a la clé, il retournera vrai.

J'espère que cela aide à une implémentation de code meilleure et plus propre

Jay Chakra
la source
1
Depuis quand !! [] est égal à faux?
Sergey
1
Belle prise. Réponse mise à jour en utilisant réduire :)
Jay Chakra
1
C'est faux. Le premier paramètre à reduceest l'accumulateur et non l' vendorobjet. Cela vérifie false.Name === "Magenic"dans chaque boucle et retourne faux
adiga
@adiga: corrigé.
Jay Chakra
1
Veuillez également vérifier la solution de Mirza Leka. Une solution beaucoup plus élégante.
Jay Chakra
13

Vous ne pouvez pas sans vraiment regarder l'objet.

Vous devriez probablement changer un peu votre structure, comme

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

Ensuite, vous pouvez simplement l'utiliser comme un hachage de recherche.

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

Peut-être trop tard, mais le tableau javascript a deux méthodes someet une everyméthode qui renvoie un booléen et peut vous aider à y parvenir.

Je pense que ce someserait plus approprié pour ce que vous avez l'intention de réaliser.

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

Certains vérifient que l'un des objets du tableau satisfait à la condition donnée.

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

Chaque valide que tous les objets du tableau satisfont à la condition donnée.

Akinjiola Toni
la source
Cela ne fonctionne pas, const array1 = [{name:'Mike'},{name:'Alice'}]; console.log(array1.every(item => item.name !== 'Mike'));cela devrait retourner vrai
Thanwa Ch.
Désolé mon pote, je voulais dire some, mettra à jour ma réponse.
Akinjiola Toni
5

Vous devez faire une boucle, il n'y a aucun moyen de contourner cela.

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

Bien sûr, vous pouvez utiliser une bibliothèque comme linq.js pour rendre cela plus agréable:

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

(voir jsFiddle pour une démo)

Je doute que linq.js soit plus rapide qu'une boucle directe, mais il est certainement plus flexible lorsque les choses deviennent un peu plus compliquées.

Tomalak
la source
5

Test des éléments du tableau:

JS propose des fonctions de tableau qui vous permettent d'y parvenir relativement facilement. Ce sont les suivants:

  1. Array.prototype.filter: Prend une fonction de rappel qui est un test, le tableau est ensuite itéré avec son rappel et filtré en fonction de ce rappel. Un nouveau tableau filtré est renvoyé.
  2. Array.prototype.some: Prend une fonction de rappel qui est un test, le tableau est ensuite itéré avec son rappel et si un élément réussit le test, le booléen true est retourné. Sinon, false est retourné

Les détails sont mieux expliqués via un exemple:

Exemple:

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

Prise en charge du navigateur:

Ces 2 fonctions sont ES6fonctionnelles, tous les navigateurs peuvent ne pas les supporter. Pour surmonter cela, vous pouvez utiliser un polyfill. Voici le polyfill pour Array.prototype.some(de 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
la source
4

si vous utilisez jquery, vous pouvez profiter de grep pour créer un tableau avec tous les objets correspondants:

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

puis utilisez le tableau de résultats:

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

Corrigez-moi si je me trompe .. j'aurais pu utiliser une forEachméthode comme celle-ci,

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

   }
});

De nos jours, j'y suis habitué, à cause de sa simplicité et de son mot explicatif. Je vous remercie.

Siddhesh Mishra
la source
1
Remarque: pas d'utilisation de retour ici
Edison
2

Vous pouvez essayer son travail pour moi.

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
la source
Eh bien, c'est une très vieille question et je pense que sa mise à jour a déjà la meilleure solution de nos jours.
Federico Galfione
1

Vous pouvez utiliser lodash . Si la bibliothèque lodash est trop lourde pour votre application, envisagez de supprimer les fonctions inutiles non utilisées.

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

Ce n'est qu'une façon de procéder. Un autre peut être:

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

console.log(arr);

L'exemple ci-dessus peut également être réécrit sans utiliser de bibliothèques comme:

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

J'espère que ma réponse vous aidera.

Abhay Shiro
la source
1

De nombreuses réponses ici sont bonnes et assez faciles. Mais si votre tableau d'objets a un ensemble de valeurs fixe, vous pouvez utiliser l'astuce ci-dessous:

Mappez tout le nom dans un objet.

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

Maintenant, ce dirtyObj vous pouvez utiliser encore et encore sans boucle.

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

Pour comparer un objet à un autre, je combine une boucle for in (utilisée pour parcourir les objets) et some (). Vous n'avez pas à vous soucier d'un tableau qui sort des limites, etc., ce qui permet d'économiser du code. La documentation sur .some peut être trouvée ici

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

Une autre façon de comparer un objet à un autre est d'utiliser une boucle imbriquée pour avec Object.keys (). Length pour obtenir la quantité d'objets dans le tableau. Code ci-dessous:

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

Pour répondre à votre question exacte, si vous recherchez simplement une valeur dans un objet, vous pouvez utiliser une seule boucle for 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');
    }
}
Paillette
la source
0

Vous pouvez également faire:

const find = (key, needle) => return !!~vendors.findIndex(v => (v[key] === needle));
BrunoWest
la source
1
vous feriez mieux de dire pourquoi il peut faire ça
Azzabi Haythem
0

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

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

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

behzad abbasi
la source
Je pense que vous vouliez dire Résultat: [{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
la source
3
S'il vous plaît et une description et assurez-vous que l'exemple que vous fournissez fonctionne .. (le filtre ne changera pas le tableau d'origine mais le clonera).
Moshe Simantov
-1

Mon approche pour résoudre ce problème consiste à utiliser ES6 et à créer une fonction qui effectue la vérification pour nous. L'avantage de cette fonction est qu'elle peut être réutilisable tout au long de votre projet pour vérifier n'importe quel tableau d'objets étant donné le keyet le valueà vérifier.

ASSEZ PARLER, VOYONS LE CODE

Array

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

Une fonction

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

Appel / Utilisation

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

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

Je préfère aller avec regex.

Si votre code est le suivant,

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

je recommanderais

/"Name":"Magenic"/.test(JSON.stringify(vendors))
sangwook kim
la source
24
Certaines personnes, confrontées à un problème, pensent "je sais, je vais utiliser des expressions régulières". Maintenant, ils ont deux problèmes.
Craicerjack
Classez ceci sous, juste parce que vous pouvez faire quelque chose, ne signifie pas que vous devriez.
Liam