web-dev-qa-db-fra.com

Comment rechercher des valeurs en double dans un tableau d'objets JavaScript et générer uniquement des valeurs uniques?

J'apprends JS. Supposons que j'ai le tableau ci-dessous d'objets:

var family = [
  {
    name: "Mike",
    age: 10
  },
  {
    name: "Matt"
    age: 13
  },
  {
    name: "Nancy",
    age: 15
  },
  {
    name: "Adam",
    age: 22
  },
  {
    name: "Jenny",
    age: 85
  },
  {
    name: "Nancy",
    age: 2
  },
  {
    name: "Carl",
    age: 40
  }
];

Notez que Nancy apparaît deux fois (ne change que l'âge). Supposons que je veuille produire uniquement des noms uniques. Comment puis-je sortir le tableau d'objets ci-dessus, sans les dupliquer? ES6 répond plus que bienvenu.

Relatif (impossible de trouver un bon moyen d'utilisation sur les objets):

EDIT Voici ce que j'ai essayé. Cela fonctionne bien avec les chaînes mais je ne vois pas comment le faire fonctionner avec des objets:

family.reduce((a, b) => {
  if (a.indexOf(b) < 0 ) {
    a.Push(b);
  }
  return a;
},[]);
4
Rico Letterman

Vous pouvez utiliser Set en combinaison avec Array#map et un opérateur spread... sur une seule ligne.

Map renvoie un tableau avec tous les noms, qui vont dans l'initialiseur de l'ensemble, puis toutes les valeurs de l'ensemble sont renvoyées dans un tableau.

var family = [{ name: "Mike", age: 10 }, { name: "Matt", age: 13 }, { name: "Nancy", age: 15 }, { name: "Adam", age: 22 }, { name: "Jenny", age: 85 }, { name: "Nancy", age: 2 }, { name: "Carl", age: 40 }],
    unique = [...new Set(family.map(a => a.name))];

console.log(unique);

Pour filtrer et renvoyer uniquement des noms uniques, vous pouvez utiliser Array#filter avec Set .

var family = [{ name: "Mike", age: 10 }, { name: "Matt", age: 13 }, { name: "Nancy", age: 15 }, { name: "Adam", age: 22 }, { name: "Jenny", age: 85 }, { name: "Nancy", age: 2 }, { name: "Carl", age: 40 }],
    unique = family.filter((set => f => !set.has(f.name) && set.add(f.name))(new Set));

console.log(unique);

22
Nina Scholz

La solution

Stocker les occurrences de name externes à la boucle dans un objet et filtrer les occurrences précédentes.

https://jsfiddle.net/nputptbb/2/

var occurrences = {}

var filteredFamily = family.filter(function(x) {
  if (occurrences[x.name]) {
    return false;
  }
  occurrences[x.name] = true;
  return true;
})

vous pouvez aussi généraliser cette solution à une fonction

function filterByProperty(array, propertyName) {
  var occurrences = {}

  return array.filter(function(x) {
    var property = x[propertyName]
    if (occurrences[property]) {
      return false;
    }
    occurrences[property]] = true;
    return true;
  })
}

et l'utiliser comme

var filteredFamily = filterByProperty(family, 'name')

Explication

Ne comparez pas les objets avec indexOf, qui utilise uniquement l'opérateur === entre les objets. La raison pour laquelle votre réponse actuelle ne fonctionne pas est que === dans JS ne compare pas les objets en profondeur, mais compare les références. Ce que je veux dire par là, vous pouvez voir dans le code suivant:

var a = { x: 1 }
var b = { x: 1 }

console.log(a === b) // false
console.log(a === a) // true

Equality vous dira si vous avez trouvé l'objet same exact, mais pas si vous avez trouvé un objet avec le même contenu.

Dans ce cas, vous pouvez comparer votre objet sur name car il doit s'agir d'une clé unique. Donc, obj.name === obj.name au lieu de obj === obj. De plus, un autre problème avec votre code qui affecte son exécution et non sa fonction est que vous utilisez un indexOf dans votre reduce. indexOf est O(n), ce qui rend la complexité de votre algorithme O(n^2). Il est donc préférable d’utiliser un objet qui a la fonction O(1) lookup.

9
m0meni

Cela fonctionnera bien.

[1, 2, 2, 3, 3, 3, 3].reduce((x, y) => x.includes(y) ? x : [...x, y], [])
2
Divyanshu Rawat

Je viens de penser à 2 façons simples pour les utilisateurs de Lodash 

Étant donné ce tableau:

let family = [
  {
    name: "Mike",
    age: 10
  },
  {
    name: "Matt",
    age: 13
  },
  {
    name: "Nancy",
    age: 15
  },
  {
    name: "Adam",
    age: 22
  },
  {
    name: "Jenny",
    age: 85
  },
  {
    name: "Nancy",
    age: 2
  },
  {
    name: "Carl",
    age: 40
  }
]

1. Trouver des doublons:

let duplicatesArr = _.difference(family, _.uniqBy(family, 'name'), 'name')

// duplicatesArr:
// [{ 
//   name: "Nancy", 
//   age: 2 
// }]

2 Recherchez s'il y a des doublons, à des fins de validation:  

let uniqArr = _.uniqBy(family, 'name')

if (uniqArr.length === family.length) {
    // No duplicates
}

if (uniqArr.length !== family.length) {
    // Has duplicates
}
0
Simon Pertersen

Avec le code que vous avez mentionné, vous pouvez essayer:

family.filter((item, index, array) => {
  return array.map((mapItem) => mapItem['name']).indexOf(item['name']) === index
})

Ou vous pouvez avoir une fonction générique pour le faire fonctionner pour d'autres tableaux d'objets:

function printUniqueResults (arrayOfObj, key) {
  return arrayOfObj.filter((item, index, array) => {
    return array.map((mapItem) => mapItem[key]).indexOf(item[key]) === index
  })
}

et puis utilisez simplement printUniqueResults(family, 'name')

(FIDDLE)

0
Cezar Augusto