390 votes

Comment puis-je regrouper un tableau d'objets par clé ?

Est-ce que quelqu'un sait s'il existe un moyen (lodash si possible également) de regrouper un tableau d'objets par une clé d'objet, puis de créer un nouveau tableau d'objets en fonction du regroupement? Par exemple, j'ai un tableau d'objets de voitures:

const cars = [
    {
        'make': 'audi',
        'model': 'r8',
        'year': '2012'
    }, {
        'make': 'audi',
        'model': 'rs5',
        'year': '2013'
    }, {
        'make': 'ford',
        'model': 'mustang',
        'year': '2012'
    }, {
        'make': 'ford',
        'model': 'fusion',
        'year': '2015'
    }, {
        'make': 'kia',
        'model': 'optima',
        'year': '2012'
    },
];

Je veux créer un nouveau tableau d'objets de voitures regroupé par make:

const cars = {
    'audi': [
        {
            'model': 'r8',
            'year': '2012'
        }, {
            'model': 'rs5',
            'year': '2013'
        },
    ],

    'ford': [
        {
            'model': 'mustang',
            'year': '2012'
        }, {
            'model': 'fusion',
            'year': '2015'
        }
    ],

    'kia': [
        {
            'model': 'optima',
            'year': '2012'
        }
    ]
}

4 votes

Votre résultat n'est pas valide.

0 votes

Y a-t-il une approche similaire pour obtenir une carte à la place d'un objet?

3 votes

Si vous utilisez Typescript (ce qui n'est pas le cas de l'OP), vous avez déjà la méthode groupBy. Vous pouvez l'utiliser en votre_array.groupBy(...)

531voto

Nina Scholz Points 17120

En JavaScript pur, vous pourriez utiliser Array#reduce avec un objet

var cars = [{ make: 'audi', model: 'r8', year: '2012' }, { make: 'audi', model: 'rs5', year: '2013' }, { make: 'ford', model: 'mustang', year: '2012' }, { make: 'ford', model: 'fusion', year: '2015' }, { make: 'kia', model: 'optima', year: '2012' }],
    result = cars.reduce(function (r, a) {
        r[a.make] = r[a.make] || [];
        r[a.make].push(a);
        return r;
    }, Object.create(null));

console.log(result);

.as-console-wrapper { max-height: 100% !important; top: 0; }

3 votes

Comment puis-je itérer les résultats de résultat ?

3 votes

Tu pourrais prendre les entrées avec Object.entries et parcourir les paires clé/valeur.

0 votes

Y a-t-il un moyen de supprimer le make des données une fois regroupées ? Cela prend de la place supplémentaire.

197voto

Jonathan Eunice Points 1314

La réponse de Timo est celle que je choisirais. Simple _.groupBy, et autorise quelques duplications dans les objets de la structure groupée.

Cependant, l'auteur de la question a également demandé que les clés de duplication make soient supprimées. Si vous voulez aller jusqu'au bout:

var grouped = _.mapValues(_.groupBy(cars, 'make'),
                          clist => clist.map(car => _.omit(car, 'make')));

console.log(grouped);

Rendu:

{ audi:
   [ { model: 'r8', year: '2012' },
     { model: 'rs5', year: '2013' } ],
  ford:
   [ { model: 'mustang', year: '2012' },
     { model: 'fusion', year: '2015' } ],
  kia: 
   [ { model: 'optima', year: '2012' } ] 
}

Si vous souhaitez le faire en utilisant Underscore.js, notez que sa version de _.mapValues s'appelle _.mapObject.

0 votes

Élégante solution!

149voto

Timo Points 22864

Vous recherchez _.groupBy().

Supprimer la propriété par laquelle vous groupez les objets devrait être trivial si nécessaire:

const cars = [{
  'make': 'audi',
  'model': 'r8',
  'year': '2012'
}, {
  'make': 'audi',
  'model': 'rs5',
  'year': '2013'
}, {
  'make': 'ford',
  'model': 'mustang',
  'year': '2012'
}, {
  'make': 'ford',
  'model': 'fusion',
  'year': '2015'
}, {
  'make': 'kia',
  'model': 'optima',
  'year': '2012'
}];

const grouped = _.groupBy(cars, car => car.make);

console.log(grouped);

40 votes

Et si vous voulez que ce soit encore plus court, var groupés = _.groupBy(voitures, 'marque'); Pas besoin d'une fonction du tout, si l'accessoir est un simple nom de propriété.

1 votes

Que signifie '_' ?

0 votes

@AdrianGrzywaczewski c'était la convention par défaut pour le nommage de l'espace de noms 'lodash' ou 'underscore'. Maintenant que les bibliothèques sont modulaires, cela n'est plus nécessaire, c'est-à-dire npmjs.com/package/lodash.groupby

137voto

metakungfu Points 820

Il n'y a absolument aucune raison de télécharger une bibliothèque tierce pour résoudre ce problème simple, comme le suggèrent les solutions ci-dessus.

La version en une ligne pour regrouper une liste d'objets par une certaine clé en es6 :

const groupByKey = (liste, clé) => liste.reduce((hash, obj) => ({...hash, [obj[clé]]:( hash[obj[clé]] || [] ).concat(obj)}), {})

La version plus longue qui filtre les objets sans la clé :

function groupByKey(tableau, clé) {
   return tableau
     .reduce((hash, obj) => {
       if(obj[clé] === undefined) return hash; 
       return Object.assign(hash, { [obj[clé]]:( hash[obj[clé]] || [] ).concat(obj)})
     }, {})
}

var voitures = [{'make':'audi','model':'r8','year':'2012'},{'make':'audi','model':'rs5','year':'2013'},{'make':'ford','model':'mustang','year':'2012'},{'make':'ford','model':'fusion','year':'2015'},{'make':'kia','model':'optima','year':'2012'    }];

console.log(groupByKey(voitures, 'make'))

REMARQUE : Il semble que la question originale demande comment regrouper les voitures par marque, mais omettre la marque dans chaque groupe. Ainsi, la réponse courte, sans bibliothèques tierces, ressemblerait à ceci :

const groupByKey = (liste, clé, {omitKey=false}) => liste.reduce((hash, {[clé]:valeur, ...rest}) => ({...hash, [valeur]:( hash[valeur] || [] ).concat(omitKey ? {...rest} : {[clé]:valeur, ...rest})} ), {})

var voitures = [{'make':'audi','model':'r8','year':'2012'},{'make':'audi','model':'rs5','year':'2013'},{'make':'ford','model':'mustang','year':'2012'},{'make':'ford','model':'fusion','year':'2015'},{'make':'kia','model':'optima','year':'2012'}];

console.log(groupByKey(voitures, 'make', {omitKey:true}))

0 votes

Ceci n'est définitivement pas de l'ES5

1 votes

Cela fonctionne simplement ! Est-ce que quelqu'un peut expliquer cette fonction de réduction ?

0 votes

J'ai aimé les deux de tes réponses, mais je vois qu'elles fournissent toutes deux le champ "make" comme membre de chaque tableau "make". J'ai fourni une réponse basée sur la tienne où la sortie fournie correspond à la sortie attendue. Merci!

11voto

nickxbs Points 81

Vous pouvez essayer de modifier l'objet à l'intérieur de la fonction appelée par _.groupBy pendant chaque itération. Veuillez noter que le tableau source modifie ses éléments !

var res = _.groupBy(cars, (car) => {
    const makeValue = car.make;
    delete car.make;
    return makeValue;
});
console.log(res);
console.log(cars);

1 votes

Bien que ce code puisse résoudre la question, inclure une explication de comment et pourquoi cela résout le problème aiderait vraiment à améliorer la qualité de votre publication. Rappelez-vous que vous répondez à la question pour les lecteurs à l'avenir, pas seulement la personne qui pose la question maintenant ! Veuillez éditer votre réponse pour ajouter une explication, et donner une indication des limitations et des hypothèses applicables.

0 votes

Il semble être la meilleure réponse pour moi car vous parcourez le tableau une seule fois pour obtenir le résultat désiré. Il n'est pas nécessaire d'utiliser une autre fonction pour supprimer la propriété make et c'est aussi plus lisible.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X