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

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

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

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.


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


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
}

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


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


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


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

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.


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.


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


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

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.


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

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.


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

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

0

Vous pouvez également faire:

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

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


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

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

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

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.