Comment obtenir des valeurs distinctes à partir d'un tableau d'objets en JavaScript?

En supposant que j'ai ce qui suit:

var array = 
    [
        {"name":"Joe", "age":17}, 
        {"name":"Bob", "age":17}, 
        {"name":"Carl", "age": 35}
    ]

Quelle est la meilleure façon d'obtenir un tableau de tous les âges distincts de sorte que j'obtienne un tableau de résultats de:

[17, 35]

Y a-t-il un moyen de structurer alternativement les données ou une meilleure méthode de sorte que je n'aurais pas à parcourir chaque tableau en vérifiant la valeur de "age" et en vérifiant par rapport à un autre tableau son existence, et l'ajouter sinon?

S'il y avait un moyen de sortir les âges distincts sans itération...

Manière inefficace actuelle que je voudrais améliorer... Si cela signifie qu'au lieu de "tableau" étant un tableau d'objets, mais une " carte "d'objets avec une clé unique (c'est-à-dire" 1,2,3"), ce serait bien aussi. Je cherche juste le moyen le plus efficace.

Ce qui suit est la façon dont je le fais actuellement, mais pour moi, l'itération semble juste être minable pour l'efficacité même si cela fonctionne...

var distinct = []
for (var i = 0; i < array.length; i++)
   if (array[i].age not in distinct)
      distinct.push(array[i].age)
189
demandé sur RB. 2013-02-28 05:33:43

26 réponses

Si C'était PHP, je construirais un tableau avec les clés et prendrais array_keys à la fin, mais JS n'a pas un tel luxe. Au lieu de cela, essayez ceci:

var flags = [], output = [], l = array.length, i;
for( i=0; i<l; i++) {
    if( flags[array[i].age]) continue;
    flags[array[i].age] = true;
    output.push(array[i].age);
}
85
répondu Niet the Dark Absol 2013-02-28 01:36:10

Si vous utilisez ES6 / ES2015 ou une version ultérieure, vous pouvez le faire de cette façon:

const unique = [...new Set(array.map(item => item.age))];

Ici est un exemple sur la façon de le faire.

274
répondu Vlad Bezden 2018-07-12 09:33:32

Vous pouvez utiliser une approche de dictionnaire comme celle-ci. Fondamentalement, vous attribuez la valeur que vous voulez être distincte comme clé dans un dictionnaire. Si la clé n'existait pas, vous ajoutez cette valeur comme distincte.

var unique = {};
var distinct = [];
for( var i in array ){
 if( typeof(unique[array[i].age]) == "undefined"){
  distinct.push(array[i].age);
 }
 unique[array[i].age] = 0;
}

Voici une démo de travail: http://jsfiddle.net/jbUKP/1

Ce sera O (n) où n est le nombre d'objets dans le tableau et m est le nombre de valeurs uniques. Il n'y a pas de moyen plus rapide que O (n) car vous devez inspecter chaque valeur au moins lorsque.

Les Performances

Http://jsperf.com/filter-versus-dictionary Quand j'ai couru ce dictionnaire était 30% plus rapide.

94
répondu Travis J 2013-02-28 01:44:03

En utilisant ES6

let array = [
  { "name": "Joe", "age": 17 },
  { "name": "Bob", "age": 17 },
  { "name": "Carl", "age": 35 }
];
array.map(item => item.age)
  .filter((value, index, self) => self.indexOf(value) === index)

> [17, 35]
69
répondu Ivan Nosov 2015-12-10 10:23:43

Je voudrais juste mapper et supprimer dups:

var ages = array.map(function(obj) { return obj.age; });
ages = ages.filter(function(v,i) { return ages.indexOf(v) == i; });

console.log(ages); //=> [17, 35]

Edit: Aight! Pas le moyen le plus efficace en termes de performance, mais L'OMI la plus simple et la plus lisible. Si vous vous souciez vraiment de la micro-optimisation ou vous avez d'énormes quantités de données ordinaire for boucle va être plus "efficace".

48
répondu elclanrs 2013-02-28 01:50:57

En utilisant les fonctionnalités ES6, vous pouvez faire quelque chose comme:

const uniqueAges = [...new Set( array.map(obj => obj.age)) ];
34
répondu Russell Vea 2018-03-07 15:31:00

Voici comment vous résoudriez cela en utilisant un nouvel ensemble via ES6 pour Typescript à partir du 25 août 2017

Array.from(new Set(yourArray.map((item: any) => item.id)))
31
répondu Christian Matthew 2017-08-25 16:50:07

La version forEach de la réponse de @ travis-j (utile sur les navigateurs modernes et le monde Node JS):

var unique = {};
var distinct = [];
array.forEach(function (x) {
  if (!unique[x.age]) {
    distinct.push(x.age);
    unique[x.age] = true;
  }
});

34% plus rapide sur Chrome v29.0.1547: http://jsperf.com/filter-versus-dictionary/3

Et une solution générique qui prend une fonction de mappeur (tad plus lent que direct map, mais c'est attendu):

function uniqueBy(arr, fn) {
  var unique = {};
  var distinct = [];
  arr.forEach(function (x) {
    var key = fn(x);
    if (!unique[key]) {
      distinct.push(key);
      unique[key] = true;
    }
  });
  return distinct;
}

// usage
uniqueBy(array, function(x){return x.age;}); // outputs [17, 35]
17
répondu Mrchief 2013-09-20 18:28:17

J'ai commencé à coller Underscore dans tous les nouveaux projets par défaut juste pour ne jamais avoir à penser à ces petits problèmes de gestion de données.

var array = [{"name":"Joe", "age":17}, {"name":"Bob", "age":17}, {"name":"Carl", "age": 35}];
console.log(_.chain(array).map(function(item) { return item.age }).uniq().value());

Produit [17, 35].

13
répondu Eevee 2013-02-28 01:45:51

En utilisant lodash

var array = [
    { "name": "Joe", "age": 17 },
    { "name": "Bob", "age": 17 },
    { "name": "Carl", "age": 35 }
];
_.chain(array).pluck('age').unique().value();
> [17, 35]
10
répondu Ivan Nosov 2015-01-21 14:01:59

Voici une autre façon de résoudre ce problème:

var result = {};
for(var i in array) {
    result[array[i].age] = null;
}
result = Object.keys(result);

Je n'ai aucune idée de la vitesse à laquelle cette solution est comparée aux autres, mais j'aime le look plus propre. ;-)


EDIT: D'accord, ce qui précède semble être la solution la plus lente de tous ici.

J'ai créé un cas de test de performance ici: http://jsperf.com/distinct-values-from-array

Au lieu de tester les âges (entiers), j'ai choisi de comparer les noms (chaînes).

La Méthode 1 (solution de TS) est très rapide. Fait intéressant, la méthode 7 surpasse toutes les autres solutions, ici je viens de me débarrasser .indexOf () et utilisé une implémentation "manuelle" de celui-ci, en évitant l'appel de fonction en boucle:

var result = [];
loop1: for (var i = 0; i < array.length; i++) {
    var name = array[i].name;
    for (var i2 = 0; i2 < result.length; i2++) {
        if (result[i2] == name) {
            continue loop1;
        }
    }
    result.push(name);
}

La différence de performance en utilisant Safari et Firefox est incroyable, et il semble que Chrome fait le meilleur travail sur l'optimisation.

Je ne sais pas exactement pourquoi les extraits ci-dessus sont si rapides par rapport aux autres, peut-être que quelqu'un de plus sage que moi a une réponse. ;-)

7
répondu BaM 2015-02-13 19:12:02

Soulignement.js _.uniq(_.pluck(array,"age"))

5
répondu Radosław Kopczyński 2016-05-02 17:24:06

En Utilisant Lodash

var array = [
    { "name": "Joe", "age": 17 },
    { "name": "Bob", "age": 17 },
    { "name": "Carl", "age": 35 }
];

_.chain(array).map('age').unique().value();

Renvoie [17,35]

4
répondu Yash Vekaria 2016-11-29 11:10:10

Je pense que vous recherchez la fonction groupBy (en utilisant Lodash)

_personsList = [{"name":"Joe", "age":17}, 
                {"name":"Bob", "age":17}, 
                {"name":"Carl", "age": 35}];
_uniqAgeList = _.groupBy(_personsList,"age");
_uniqAges = Object.keys(_uniqAgeList);

Produit le résultat:

17,35

Démo JsFiddle: http://jsfiddle.net/4J2SX/201/

4
répondu Amil Sajeev 2017-05-10 12:16:16
function get_unique_values_from_array_object(array,property){
    var unique = {};
    var distinct = [];
    for( var i in array ){
       if( typeof(unique[array[i][property]]) == "undefined"){
          distinct.push(array[i]);
       }
       unique[array[i][property]] = 0;
    }
    return distinct;
}
4
répondu Dmitriy Kupriyanov 2017-06-29 19:55:28

Je viens de trouver ceci et je pensais que c'était utile

_.map(_.indexBy(records, '_id'), function(obj){return obj})

Encore une fois en utilisant underscore , donc si vous avez un objet comme celui-ci

var records = [{_id:1,name:'one', _id:2,name:'two', _id:1,name:'one'}]

, Il vous donnera les objets uniques seulement.

Ce qui se passe ici est que indexBy retourne une carte

{ 1:{_id:1,name:'one'}, 2:{_id:2,name:'two'} }

Et juste parce que c'est une carte, toutes les clés sont uniques.

Ensuite, je suis juste en train de mapper cette liste au tableau.

Au cas où vous n'auriez besoin que des valeurs distinctes

_.map(_.indexBy(records, '_id'), function(obj,key){return key})

Gardez à l'esprit que la key est retourné comme une chaîne donc, si vous avez besoin d'entiers à la place, vous devriez faire

_.map(_.indexBy(records, '_id'), function(obj,key){return parseInt(key)})
2
répondu simo 2014-07-23 12:10:31

Si vous avez un tableau.prototype.comprend ou sont prêts à polyfill, cela fonctionne:

var ages = []; array.forEach(function(x) { if (!ages.includes(x.age)) ages.push(x.age); });
2
répondu quillbreaker 2015-05-20 23:13:22

Si comme moi vous préférez un plus" fonctionnel " sans compromettre la vitesse, cet exemple utilise la recherche de dictionnaire rapide enveloppé dans réduire la fermeture.

var array = 
[
    {"name":"Joe", "age":17}, 
    {"name":"Bob", "age":17}, 
    {"name":"Carl", "age": 35}
]
var uniqueAges = array.reduce((p,c,i,a) => {
    if(!p[0][c.age]) {
        p[1].push(p[0][c.age] = c.age);
    }
    if(i<a.length-1) {
        return p
    } else {
        return p[1]
    }
}, [{},[]])

Selon ce test ma solution est deux fois plus rapide que la réponse proposée

2
répondu bmaggi 2016-01-31 06:54:21

Il y a déjà beaucoup de réponses valides, mais je voulais en ajouter une qui utilise uniquement la méthode reduce() car elle est propre et simple.

function uniqueBy(arr, prop){
  return arr.reduce((a, d) => {
    if (!a.includes(d[prop])) a.push(d[prop]);
    return a;
  }, []);
}

Utilisez-le comme ceci:

var array = [
  {"name": "Joe", "age": 17}, 
  {"name": "Bob", "age": 17}, 
  {"name": "Carl", "age": 35}
];

var ages = uniqueBy(array, "age");
console.log(ages); // [17, 35]
2
répondu Harry Stevens 2018-08-02 13:39:58

Voici une solution polyvalente qui utilise reduce, permet le mappage et maintient l'ordre d'insertion.

les éléments de: Un tableau

mapper : une fonction unaire qui mappe l'élément aux critères, ou vide pour mapper l'élément lui-même.

function distinct(items, mapper) {
    if (!mapper) mapper = (item)=>item;
    return items.map(mapper).reduce((acc, item) => {
        if (acc.indexOf(item) === -1) acc.push(item);
        return acc;
    }, []);
}

Utilisation

const distinctLastNames = distinct(items, (item)=>item.lastName);
const distinctItems = distinct(items);

Vous pouvez ajouter ceci à votre prototype de tableau et laisser de côté le paramètre items si c'est votre style...

const distinctLastNames = items.distinct( (item)=>item.lastName) ) ;
const distinctItems = items.distinct() ;

Vous pouvez également utiliser un ensemble au lieu d'un tableau pour correspondre.

function distinct(items, mapper) {
    if (!mapper) mapper = (item)=>item;
    return items.map(mapper).reduce((acc, item) => {
        acc.add(item);
        return acc;
    }, new Set());
}
1
répondu Steven Spungin 2017-01-19 21:18:55

Mes deux cents sur cette fonction:

var result = [];
for (var len = array.length, i = 0; i < len; ++i) {
  var age = array[i].age;
  if (result.indexOf(age) > -1) continue;
  result.push(age);
}

Vous pouvez voir le résultat ici (Méthode 8) http://jsperf.com/distinct-values-from-array/3

0
répondu Dementic 2015-03-31 13:17:27

L'utilisation de nouvelles fonctionnalités Ecma est géniale, mais tous les utilisateurs n'en ont pas encore disponibles.

Le code suivant attachera une nouvelle fonction nommée distinct à l'objet tableau Global. Si vous essayez d'obtenir les valeurs distinctes d'un tableau d'objets, vous pouvez passer le nom de la valeur pour obtenir les valeurs distinctes de ce type.

Array.prototype.distinct = function(item){   var results = [];
for (var i = 0, l = this.length; i < l; i++)
    if (!item){
        if (results.indexOf(this[i]) === -1)
            results.push(this[i]);
        } else {
        if (results.indexOf(this[i][item]) === -1)
            results.push(this[i][item]);
    }
return results;};

Consultez mon message dans CodePen pour une démo.

0
répondu Hasan Savran 2017-03-28 17:59:50

Essayez simplement

var x = [] ;
for (var i = 0 ; i < array.length ; i++)
{
 if(x.indexOf(array[i]['age']) == -1)
  {
    x.push(array[i]['age']);
  }
}
console.log(x);
0
répondu Pravin Pareek 2017-04-14 12:06:36

En utilisant d3.js v3 :

  ages = d3.set(
    array.map(function (d) { return d.age; })
  ).values();
0
répondu alex.rind 2017-07-26 11:52:19
unique(obj, prop) {
    let result = [];
    let seen = new Set();

    Object.keys(obj)
        .forEach((key) => {
            let value = obj[key];

            let test = !prop
                ? value
                : value[prop];

            !seen.has(test)
                && seen.add(test)
                && result.push(value);
        });

    return result;
}
0
répondu Devonte 2017-10-10 22:48:56

Cette fonction peut unique tableau et objet

function oaunic(x,n=0){
    if(n==0) n = "elem";
    else n = "elem."+n;
    var uval = [];
    var unic = x.filter(function(elem, index, self){
        if(uval.indexOf(eval(n)) < 0){
            uval.push(eval(n));
            return index == self.indexOf(elem);
        }
    })
    return unic;
}

Utilisez-le comme ceci

tags_obj = [{name:"milad"},{name:"maziar"},{name:"maziar"}]
tags_arr = ["milad","maziar","maziar"]
console.log(oaunic(tags_obj,"name")) //for object
console.log(oaunic(tags_arr)) //for array
0
répondu milad 2017-11-09 06:09:49