El método más eficiente para agrupar una serie de objetos

Resuelto D'Arcy Rail-Ip asked hace 11 años • 62 respuestas

¿Cuál es la forma más eficaz de agrupar objetos en una matriz?

Por ejemplo, dada esta matriz de objetos:

[ 
    { 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" }
]

Estoy mostrando esta información en una tabla. Me gustaría agrupar por diferentes métodos, pero quiero sumar los valores.

Estoy usando Underscore.js para su función groupby, que es útil, pero no funciona todo, porque no quiero que se "dividan" sino que se "fusionen", más como el group bymétodo SQL.

Lo que estoy buscando podría sumar valores específicos (si se solicita).

Entonces, si hiciera groupby Phase, me gustaría recibir:

[
    { Phase: "Phase 1", Value: 50 },
    { Phase: "Phase 2", Value: 130 }
]

Y si hiciera groupy Phase/ Step, recibiría:

[
    { 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 una secuencia de comandos útil para esto, o debería seguir usando Underscore.js y luego recorrer el objeto resultante para hacer los totales yo mismo?

D'Arcy Rail-Ip avatar Jan 22 '13 03:01 D'Arcy Rail-Ip
Aceptado

Si desea evitar bibliotecas externas, puede implementar de manera concisa una versión básica como groupBy()esta:

var groupBy = function(xs, key) {
  return xs.reduce(function(rv, x) {
    (rv[x[key]] = rv[x[key]] || []).push(x);
    return rv;
  }, {});
};

console.log(groupBy(['one', 'two', 'three'], 'length'));

// => {"3": ["one", "two"], "5": ["three"]}
Expandir fragmento

Ceasar avatar Jan 20 '2016 02:01 Ceasar

Usando el objeto de mapa ES6:

/**
 * @description
 * Takes an Array<V>, and a grouping function,
 * and returns a Map of the array grouped by the grouping function.
 *
 * @param list An array of type V.
 * @param keyGetter A Function that takes the the Array type V as an input, and returns a value of type K.
 *                  K is generally intended to be a property key of V.
 *
 * @returns Map of the array grouped by the grouping function.
 */
//export function groupBy<K, V>(list: Array<V>, keyGetter: (input: V) => K): Map<K, Array<V>> {
//    const map = new Map<K, Array<V>>();
function groupBy(list, keyGetter) {
    const map = new Map();
    list.forEach((item) => {
         const key = keyGetter(item);
         const collection = map.get(key);
         if (!collection) {
             map.set(key, [item]);
         } else {
             collection.push(item);
         }
    });
    return map;
}


// example usage

const pets = [
    {type:"Dog", name:"Spot"},
    {type:"Cat", name:"Tiger"},
    {type:"Dog", name:"Rover"}, 
    {type:"Cat", name:"Leo"}
];
    
const grouped = groupBy(pets, pet => pet.type);
    
console.log(grouped.get("Dog")); // -> [{type:"Dog", name:"Spot"}, {type:"Dog", name:"Rover"}]
console.log(grouped.get("Cat")); // -> [{type:"Cat", name:"Tiger"}, {type:"Cat", name:"Leo"}]

const odd = Symbol();
const even = Symbol();
const numbers = [1,2,3,4,5,6,7];

const oddEven = groupBy(numbers, x => (x % 2 === 1 ? odd : even));
    
console.log(oddEven.get(odd)); // -> [1,3,5,7]
console.log(oddEven.get(even)); // -> [2,4,6]
Expandir fragmento

Acerca del mapa: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map

mortb avatar Jul 12 '2016 11:07 mortb

con ES6:

const groupBy = (items, key) => items.reduce(
  (result, item) => ({
    ...result,
    [item[key]]: [
      ...(result[item[key]] || []),
      item,
    ],
  }), 
  {},
);
Joseph Nields avatar Sep 26 '2017 16:09 Joseph Nields