Quelle est la manière la plus efficace de grouper des objets dans un tableau?
Par exemple, étant donné ce tableau d'objets:
[
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
]
J'affiche ces informations dans un tableau. Je voudrais regrouper différentes méthodes, mais je veux additionner les valeurs.
J'utilise Underscore.js pour sa fonction groupby, ce qui est utile, mais ne fait pas tout le tour, parce que je ne veux pas qu'ils soient "divisés" mais "fusionnés", plus comme la group by
méthode SQL .
Ce que je recherche serait en mesure de totaliser des valeurs spécifiques (si demandé).
Donc, si je faisais du groupby Phase
, je voudrais recevoir:
[
{ Phase: "Phase 1", Value: 50 },
{ Phase: "Phase 2", Value: 130 }
]
Et si je faisais du groupy Phase
/ Step
, je recevrais:
[
{ Phase: "Phase 1", Step: "Step 1", Value: 15 },
{ Phase: "Phase 1", Step: "Step 2", Value: 35 },
{ Phase: "Phase 2", Step: "Step 1", Value: 55 },
{ Phase: "Phase 2", Step: "Step 2", Value: 75 }
]
Existe-t-il un script utile pour cela, ou devrais-je m'en tenir à utiliser Underscore.js, puis parcourir en boucle l'objet résultant pour faire les totaux moi-même?
var groupBy = function<TItem>(xs: TItem[], key: string) : {[key: string]: TItem[]} { ...
Utilisation de l'objet Carte ES6:
À propos de Map: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map
la source
get()
méthode? qui est je veux que la sortie soit affichée sans passer la cléconsole.log(grouped.entries());
dans l'exemple jsfiddle, il retourne un itérable qui se comporte comme un tableau de clés + valeurs. Pouvez-vous essayer cela et voir si cela aide?console.log(Array.from(grouped));
Array.from(groupBy(jsonObj, item => i.type)).map(i => ( {[i[0]]: i[1].length} ))
avec ES6:
la source
...result
. Maintenant je ne peux pas dormir à cause de ça.Bien que la réponse linq soit intéressante, elle est également assez lourde. Mon approche est quelque peu différente:
Vous pouvez le voir en action sur JSBin .
Je n'ai rien vu dans Underscore qui fasse ce qui se
has
passe, bien que je puisse le manquer. C'est à peu près la même chose_.contains
, mais utilise_.isEqual
plutôt que===
pour des comparaisons. En dehors de cela, le reste est spécifique au problème, bien qu'avec une tentative d'être générique.DataGrouper.sum(data, ["Phase"])
Retourne maintenantEt
DataGrouper.sum(data, ["Phase", "Step"])
retourneMais ce
sum
n'est qu'une fonction potentielle ici. Vous pouvez en enregistrer d'autres à votre guise:et maintenant
DataGrouper.max(data, ["Phase", "Step"])
je reviendraiou si vous l'avez enregistré:
puis appeler
DataGrouper.tasks(data, ["Phase", "Step"])
vousDataGrouper
lui-même est une fonction. Vous pouvez l'appeler avec vos données et une liste des propriétés que vous souhaitez regrouper. Il renvoie un tableau dont les éléments sont des objets avec deux propriétés:key
est la collection de propriétés groupées,vals
est un tableau d'objets contenant les propriétés restantes qui ne sont pas dans la clé. Par exemple,DataGrouper(data, ["Phase", "Step"])
donnera:DataGrouper.register
accepte une fonction et crée une nouvelle fonction qui accepte les données initiales et les propriétés à regrouper. Cette nouvelle fonction prend ensuite le format de sortie comme ci-dessus et exécute votre fonction contre chacun d'eux à son tour, en retournant un nouveau tableau. La fonction générée est stockée en tant que propriété deDataGrouper
selon un nom que vous fournissez et est également renvoyée si vous souhaitez simplement une référence locale.Eh bien, c'est beaucoup d'explications. Le code est assez simple, j'espère!
la source
Je vérifierais le groupe lodash il semble faire exactement ce que vous recherchez. Il est également assez léger et très simple.
Exemple de violon: https://jsfiddle.net/r7szvt5k/
À condition que le nom de votre tableau soit
arr
le groupBy avec lodash, c'est juste:la source
Cela se fait probablement plus facilement avec
linq.js
, qui est destiné à être une véritable implémentation de LINQ en JavaScript ( DEMO ):résultat:
Ou, plus simplement en utilisant les sélecteurs basés sur des chaînes ( DEMO ):
la source
GroupBy(function(x){ return x.Phase; })
Vous pouvez construire un ES6 à
Map
partir dearray.reduce()
.Cela présente quelques avantages par rapport aux autres solutions:
_.groupBy()
)Map
plutôt qu'un objet (par exemple tel que renvoyé par_.groupBy()
). Cela présente de nombreux avantages , notamment:Map
est un résultat plus utile qu'un tableau de tableaux. Mais si vous voulez un tableau de tableaux, vous pouvez alors appelerArray.from(groupedMap.entries())
(pour un tableau de[key, group array]
paires) ouArray.from(groupedMap.values())
(pour un tableau simple de tableaux).Comme exemple du dernier point, imaginez que j'ai un tableau d'objets sur lequel je veux faire une fusion (superficielle) par id, comme ceci:
Pour ce faire, je commence généralement par regrouper par id, puis par fusionner chacun des tableaux résultants. Au lieu de cela, vous pouvez effectuer la fusion directement dans
reduce()
:la source
a.reduce(function(em, e){em.set(e.id, (em.get(e.id)||[]).concat([e]));return em;}, new Map())
, environ)De: http://underscorejs.org/#groupBy
la source
Vous pouvez le faire avec la bibliothèque JavaScript Alasql :
Essayez cet exemple sur jsFiddle .
BTW: Sur de grands tableaux (100 000 enregistrements et plus) Alasql plus rapide que Linq. Voir test sur jsPref .
Commentaires:
la source
la source
MDN a cet exemple dans sa
Array.reduce()
documentation.la source
Bien que la question ait des réponses et que les réponses semblent un peu trop compliquées, je suggère d'utiliser Javascript vanilla pour le regroupement avec un imbriqué (si nécessaire)
Map
.la source
Sans mutations:
la source
Cette solution prend n'importe quelle fonction arbitraire (pas une clé), elle est donc plus flexible que les solutions ci-dessus et autorise les fonctions fléchées , qui sont similaires aux expressions lambda utilisées dans LINQ :
REMARQUE: vous décidez si vous souhaitez étendre
Array
le prototype de.Exemple pris en charge dans la plupart des navigateurs:
Exemple d'utilisation des fonctions fléchées (ES6):
Les deux exemples ci-dessus renvoient:
la source
let key = 'myKey'; let newGroupedArray = myArrayOfObjects.reduce(function (acc, val) { (acc[val[key]] = acc[val[key]] || []).push(val); return acc;});
je voudrais suggérer mon approche. Premièrement, regroupement et agrégation séparés. Permet de déclarer la fonction prototypique "group by". Il faut une autre fonction pour produire une chaîne de «hachage» pour chaque élément de tableau à regrouper.
lorsque le regroupement est effectué, vous pouvez agréger les données selon vos besoins, dans votre cas
en commun ça marche. j'ai testé ce code dans la console chrome. et n'hésitez pas à vous améliorer et à trouver des erreurs;)
la source
map[_hash(key)].key = key;
tomap[_hash(key)].key = _hash(key);
.Imaginez que vous ayez quelque chose comme ça:
[{id:1, cat:'sedan'},{id:2, cat:'sport'},{id:3, cat:'sport'},{id:4, cat:'sedan'}]
En faisant cela:
const categories = [...new Set(cars.map((car) => car.cat))]
Vous obtiendrez ceci:
['sedan','sport']
Explication: 1. Premièrement, nous créons un nouvel ensemble en passant un tableau. Étant donné que Set n'autorise que des valeurs uniques, tous les doublons seront supprimés.
Définissez Doc: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set Spread OperatorDoc: https://developer.mozilla.org/en-US/docs/Web/JavaScript / Référence / Opérateurs / Spread_syntax
la source
Réponse vérifiée - juste un regroupement superficiel. C'est assez agréable de comprendre la réduction. La question pose également le problème des calculs agrégés supplémentaires.
Voici un REAL GROUP BY pour Array of Objects par certains champs avec 1) le nom de clé calculé et 2) une solution complète pour la mise en cascade du regroupement en fournissant la liste des clés souhaitées et en convertissant ses valeurs uniques en clés racine comme SQL GROUP BY le fait.
Jouez avec
estKey
- vous pouvez regrouper par plus d'un champ, ajouter des agrégations, des calculs ou d'autres traitements supplémentaires.Vous pouvez également regrouper les données de manière récursive. Par exemple, groupez d'abord par
Phase
, puis parStep
champ, etc. Soufflez également les données de repos graisseux.la source
Celui-ci génère un tableau.
la source
Basé sur les réponses précédentes
et c'est un peu plus agréable à regarder avec la syntaxe de propagation d'objet, si votre environnement le prend en charge.
Ici, notre réducteur prend la valeur de retour partiellement formée (en commençant par un objet vide) et retourne un objet composé des membres étalés de la valeur de retour précédente, ainsi qu'un nouveau membre dont la clé est calculée à partir de la valeur de l'itéré en cours à
prop
et dont la valeur est une liste de toutes les valeurs de cet accessoire avec la valeur actuelle.la source
la source
Voici une solution désagréable et difficile à lire avec ES6:
Pour ceux qui demandent comment ce même travail, voici une explication:
Dans les deux,
=>
vous avez unreturn
La
Array.prototype.reduce
fonction prend jusqu'à 4 paramètres. C'est pourquoi un cinquième paramètre est ajouté afin que nous puissions avoir une déclaration de variable bon marché pour le groupe (k) au niveau de la déclaration de paramètre en utilisant une valeur par défaut. (oui, c'est de la sorcellerie)Si notre groupe actuel n'existe pas sur l'itération précédente, nous créons un nouveau tableau vide
((r[k] || (r[k] = []))
Cela évaluera à l'expression la plus à gauche, en d'autres termes, un tableau existant ou un tableau vide , c'est pourquoi il y a un immédiatpush
après cette expression, car de toute façon, vous obtiendrez un tableau.Lorsqu'il y a un
return
, l',
opérateur virgule supprimera la valeur la plus à gauche, renvoyant le groupe précédent modifié pour ce scénario.Une version plus facile à comprendre qui fait de même est:
la source
Permet de générer un
Array.prototype.groupBy()
outil générique . Pour plus de variété, utilisons ES6 fanciness l'opérateur de propagation pour une correspondance de motifs haskellesque sur une approche récursive. Faisons également en sorteArray.prototype.groupBy()
d'accepter un rappel qui prend comme élément l' argument item (e
), l'index (i
) et le tableau appliqué (a
).la source
La réponse de Ceasar est bonne, mais ne fonctionne que pour les propriétés internes des éléments à l'intérieur du tableau (longueur en cas de chaîne).
cette implémentation fonctionne plus comme: ce lien
J'espère que cela t'aides...
la source
De @mortb, @jmarceli réponse et de ce post ,
J'en profite
JSON.stringify()
pour être l'identité des multiples colonnes de PRIMITIVE VALUE de group by.Sans tiers
Avec Lodash tiers
la source
Version
reduce
basée sur ES6 version avec prise en charge de la fonctioniteratee
.Fonctionne comme prévu si la
iteratee
fonction n'est pas fournie:Dans le cadre de la question OP:
Une autre version ES6 qui inverse le regroupement et utilise les
values
askeys
et leskeys
as lesgrouped values
:Remarque: les fonctions sont affichées sous leur forme abrégée (une ligne) par souci de concision et pour ne relier que l'idée. Vous pouvez les développer et ajouter une vérification d'erreur supplémentaire, etc.
la source
Je vérifierais déclarative-js,
groupBy
il semble faire exactement ce que vous recherchez. C'est aussi:la source
la source
Voici une version ES6 qui ne se cassera pas sur les membres nuls
la source
Pour ajouter à la réponse de Scott Sauyet , certaines personnes demandaient dans les commentaires comment utiliser sa fonction pour grouper valeur1, valeur2, etc., au lieu de regrouper une seule valeur.
Il suffit d'éditer sa fonction somme:
laissant le principal (DataGrouper) inchangé:
la source
Avec fonction de tri
Échantillon:
la source