Sumar claves similares en una matriz de objetos.

Resuelto Sameer Joshi asked hace 10 años • 19 respuestas

Tengo una serie de objetos como los siguientes:

[
    {
        'name': 'P1',
        'value': 150
    },
    {
        'name': 'P1',
        'value': 150
    },
    {
        'name': 'P2',
        'value': 200
    },
    {
        'name': 'P3',
        'value': 450
    }
]

Necesito sumar todos los valores de los objetos con el mismo nombre. (Probablemente también otras operaciones matemáticas como calcular el promedio). Para el ejemplo anterior, el resultado sería:

[
    {
        'name': 'P1',
        'value': 300
    },
    {
        'name': 'P2',
        'value': 200
    },
    {
        'name': 'P3',
        'value': 450
    }
]
Sameer Joshi avatar Jun 27 '14 12:06 Sameer Joshi
Aceptado

Primero recorra la matriz e inserte el 'nombre' en la propiedad de otro objeto. Si la propiedad existe, agregue el 'valor' al valor de la propiedad; de lo contrario, inicialice la propiedad al 'valor'. Una vez que cree este objeto, repita las propiedades y envíelas a otra matriz.

Aquí hay un código:

var obj = [
    { 'name': 'P1', 'value': 150 },
    { 'name': 'P1', 'value': 150 },
    { 'name': 'P2', 'value': 200 },
    { 'name': 'P3', 'value': 450 }
];

var holder = {};

obj.forEach(function(d) {
  if (holder.hasOwnProperty(d.name)) {
    holder[d.name] = holder[d.name] + d.value;
  } else {
    holder[d.name] = d.value;
  }
});

var obj2 = [];

for (var prop in holder) {
  obj2.push({ name: prop, value: holder[prop] });
}

console.log(obj2);
Expandir fragmento

Espero que esto ayude.

link avatar Jun 27 '2014 05:06 link

Un enfoque ES2024 para agrupar porname

Primero puede usar Map.groupBypara agrupar su matriz namedonde Mapcada clave en el mapa es una namede sus objetos y el valor es una matriz de objetos de su matriz con esa nameclave. Luego puede usar Array.from()el grupo Mappara convertirlo en una matriz de objetos, donde para cada objeto, acumula los objetos para sumar valuecon la misma nameclave usando .reduce():

const sumByKey = (arr, key, value) => {
  const grouped = Map.groupBy(arr, o => o[key]);
  return Array.from(
    grouped.values(),
    group => group.reduce((acc, obj) => ({...obj, [value]: (acc[value] ?? 0) + obj[value]}), {})
  );
}

const arr = [ { 'name': 'P1', 'value': 150 }, { 'name': 'P1', 'value': 150 }, { 'name': 'P2', 'value': 200 }, { 'name': 'P3', 'value': 450 } ];
console.log(sumByKey(arr, 'name', 'value')); // [ { "name": "P1", "value": 300 }, { "name": "P2", "value": 200 }, { "name": "P3", "value": 450 } ]
Expandir fragmento

Un enfoque ES6 para agrupar por name:

Puede convertir su conjunto de objetos a Mapusando .reduce(). El mapa tiene pares clave-valor, donde cada clave es namey cada una valuees la suma acumulada de valores para esa nameclave en particular. Luego puede convertir fácilmente el Mapa nuevamente en una matriz usando Array.from(), donde puede proporcionar una función de mapeo que tomará las claves/valores del Mapa y los convertirá en objetos:

const arr = [ { 'name': 'P1', 'value': 150 }, { 'name': 'P1', 'value': 150 }, { 'name': 'P2', 'value': 200 }, { 'name': 'P3', 'value': 450 } ];

const res = Array.from(arr.reduce(
  (m, {name, value}) => m.set(name, (m.get(name) || 0) + value), new Map
), ([name, value]) => ({name, value}));
console.log(res);
Expandir fragmento

Lo anterior es bastante compacto y no necesariamente el más fácil de leer. Sugeriría ponerlo en una función para que quede más claro lo que está haciendo. Si busca más código autodocumentado, usarlo for...ofpuede hacer que lo anterior sea más fácil de entender. La siguiente función también es útil si desea agrupar o sumar claves con espacios:

const arr = [ { 'name': 'P1', 'value': 150 }, { 'name': 'P1', 'value': 150 }, { 'name': 'P2', 'value': 200 }, { 'name': 'P3', 'value': 450 } ];

const sumByKey = (arr, key, value) => {
  const map = new Map();
  for(const obj of arr) {
    const currSum = map.get(obj[key]) || 0;
    map.set(obj[key], currSum + obj[value]);
  }
  const res = Array.from(map, ([k, v]) => ({[key]: k, [value]: v}));
  return res;
}

console.log(sumByKey(arr, 'name', 'value')); // 'name' = value to group by, 'value' = value to sum
Expandir fragmento


Agrupar por algo más que solo name:

Aquí hay un enfoque que debería funcionar si tiene otras propiedades superpuestas además de solo name(las claves/valores deben ser los mismos en ambos objetos para que se "agrupen"). Implica iterar a través de su matriz y reducirla a Map que contiene pares clave-valor. Cada clave del nuevo Mapa es una cadena de todos los valores de propiedad por los que desea agrupar y, por lo tanto, si la clave de su objeto ya existe, entonces sabrá que es un duplicado, lo que significa que puede agregar el valor actual del objeto al objeto almacenado. . Finalmente, puede utilizar Array.from()para transformar su mapa de pares clave-valor en una matriz de valores justos:

const arr = [{'name':'P1','value':150},{'name':'P1','value':150},{'name':'P2','value':200},{'name':'P3','value':450}];

const res = Array.from(arr.reduce((acc, {value, ...r}) => {
  const key = JSON.stringify(r);
  const current = acc.get(key) || {...r, value: 0};  
  return acc.set(key, {...current, value: current.value + value});
}, new Map).values());
console.log(res);
Expandir fragmento

Nick Parsons avatar Aug 13 '2019 12:08 Nick Parsons

Puede utilizar Array.reduce() para acumular resultados durante cada iteración.

var arr = [{'name':'P1','value':150},{'name':'P1','value':150},{'name':'P2','value':200},{'name':'P3','value':450}];

var result = arr.reduce(function(acc, val){
    var o = acc.filter(function(obj){
        return obj.name==val.name;
    }).pop() || {name:val.name, value:0};
    
    o.value += val.value;
    acc.push(o);
    return acc;
},[]);

console.log(result);
Expandir fragmento

Vignesh Raja avatar Oct 23 '2018 04:10 Vignesh Raja