javascript - new - Suma todos los datos en una matriz de objetos en una nueva matriz de objetos
recorrer array de objetos javascript (14)
Tengo una matriz de objetos que se parece a esto:
var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}]
y quiero sumar cada elemento de la matriz para producir una matriz como esta:
var result = [{costOfAirtickets: 4000, costOfHotel: 3200}]
He utilizado un mapa y una función de reducción, pero solo pude sumar un elemento individual así:
data.map(item => ite.costOfAirtickets).reduce((prev, next)=>prev + next); // 22
En este momento, esto produce un valor único que no es lo que quiero según la explicación inicial.
¿Hay una manera de hacer esto en Javascript o probablemente con lodash.
Aquí hay un enfoque lodash
_(data).flatMap(_.entries).groupBy(0).mapValues(v=>_.sumBy(v, 1)).value()
Se sumará por todas las claves únicas.
var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];
var res = _(data).flatMap(_.entries).groupBy(0).mapValues(v=>_.sumBy(v, 0)).value();
console.log(res);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Envuelva su resultado en un [...]
o use un .castArray()
al final antes de desenvolverlo utilizando .value()
en caso de que desee una matriz como resultado.
Este es el enfoque más fácil que podría pensar
var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];
var sum ={};
for(var obj in data){
for(var ele in data[obj]){
if(!data[obj].hasOwnProperty(ele)) continue;
if(sum[ele] === undefined){
sum[ele] = data[obj][ele];
}else{
sum[ele] = sum[ele] + data[obj][ele];
}
}
}
var arr = [sum];
console.log(arr);
Intenta usar reducir solo como se muestra en el siguiente fragmento de código. Intenta evitar múltiples iteraciones. Espero que el fragmento de abajo ayuda!
var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}]
var total = data.reduce(function (result,value,key) {
result[''costOfAirtickets''] = result[''costOfAirtickets''] + value[''costOfAirtickets''];
result[''costOfHotel''] = result[''costOfHotel''] + value[''costOfHotel''];
return result},{costOfAirtickets:0,costOfHotel:0});
console.log(total)
Mi respuesta simplista sería como;
var data = [{costOfAirtickets: 2500, costOfHotel: 1200},
{costOfAirtickets: 1500, costOfHotel: 1000}],
res = data.reduce((p,c) => Object.entries(c).reduce((r,[k,v]) => (r[k]+=v, r), p));
console.log(res);
Una nota sobre el uso de .reduce()
: si los elementos de la matriz y el valor acumulado son del mismo tipo, puede considerar no utilizar un valor inicial como acumulador y hacer su reducción con los elementos anterior ( p
) y actual ( c
). La reducción exterior en el fragmento anterior es de este tipo. Sin embargo, la reducción interna toma una matriz del par clave ( k
) valor ( v
) como [k,v]
para devolver un objeto, por lo tanto, un valor inicial ( p
) es esencial.
El resultado es el valor acumulado como un objeto. Si lo necesita en una matriz, simplemente póngalo en una matriz como [res]
.
No necesitas mapear la matriz, en realidad solo estás reduciendo las cosas dentro de ella.
const totalCostOfAirTickets: data.reduce((prev, next) => prev + next.costOfAirTickets, 0)
const totalCostOfHotel: data.reduce((prev, next) => prev + next.costOfHotel, 0)
const totals = [{totalCostOfAirTickets, totalCostOfHotel}]
De una vez, podrías hacer algo como
const totals = data.reduce((prev, next) => {
prev.costOfAirTickets += next.costOfAirTickets;
prev.costOfHotel += next.costOfHotel;
}, {costOfAirTickets: 0, costOfHotel: 0})
Otra solución sería usar Map
( no Array.prototype.map ) ya que tiene varias diferencias notables en comparación con los objetos :
var data = [{
costOfAirtickets: 2500,
costOfHotel: 1200
}, {
costOfAirtickets: 1500,
costOfHotel: 1000
}]
let sums = data.reduce((collection,rcd)=>{
Object.entries(rcd).forEach(([key,value])=>{
let sum = collection.get(key) || 0
collection.set(key, sum + +value)
})
return collection
}, new Map())
console.log(...sums.entries())
Explicación
Bucle externo
Lo anterior primero itera sobre su matriz de data
usando el método de reduce
. Cada objeto dentro del cual me rcd
como un registro, se distingue en el código a través de la variable, rcd
.
Cada iteración de reduce devuelve un valor que se pasa como primer argumento a la siguiente iteración del bucle. En este caso, la collection
parámetros contiene ese argumento, que es su conjunto de sumas.
Bucle interno
Dentro del ciclo de reduce
, cada par clave / valor del registro se itera sobre el uso de forEach
. Para obtener el par clave / valor se Object.entries
método Object.entries
. El uso de la desestructuración de matrices de estos argumentos se puede asignar directamente a las variables respectivas, key
y value
Recuperando / estableciendo valores
A diferencia de un objeto primitivo, Map
tiene sus propios métodos para obtener y configurar sus entradas utilizando get()
y set()
. Así que primero recupere la suma anterior usando get()
, si no está establecida, entonces el valor predeterminado es 0
, que es lo que || 0
|| 0
hace. En ese momento, puede asumir que la suma anterior es al menos 0 o mayor y agregarle el valor de la clave actual.
Alternativas al Mapa
Si encuentra que Map es un poco torpe, también puede usar un objeto similar como Set , que tiene muchos de los mismos métodos (excepto el get()
), o también podría usar un objeto primitivo (es decir, {}
) .
Para crear un valor resultante / reducido, debe usar el método .reduce()
lugar de .map()
:
let data = [
{costOfAirtickets: 2500, costOfHotel: 1200},
{costOfAirtickets: 1500, costOfHotel: 1000}
];
let result = data.reduce(
(a, c) => (Object.keys(c).forEach(k => (a[k] = (a[k] || 0) + c[k])), a), {}
);
console.log(result);
Podría reducir la matriz tomando un objeto para sumar.
var data = [{ costOfAirtickets: 2500, costOfHotel: 1200 }, { costOfAirtickets: 1500, costOfHotel: 1000 }],
keys = [''costOfAirtickets'', ''costOfHotel''],
sum = data.reduce((r, o) => {
keys.forEach(k => r[k] += o[k]);
return r;
}, Object.assign(...keys.map(k => ({ [k]: 0 }))));
console.log(sum);
Puedes usar un simple bucle forEach()
para eso:
var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];
var res = [];
var tempObj = {};
data.forEach(({costOfAirtickets, costOfHotel}) => {
tempObj[''costOfAirtickets''] = (tempObj[''costOfAirtickets''] || 0) + costOfAirtickets;
tempObj[''costOfHotel''] = (tempObj[''costOfHotel''] || 0) + costOfHotel;
});
res.push(tempObj);
console.log(res);
Usa Lodash para simplificar tu vida.
const _ = require(''lodash'')
let keys = [''costOfAirtickets'', ''costOfHotel''];
let results = _.zipObject(keys, keys.map(key => _.sum( _.map(data, key))))
...
{ costOfAirtickets: 4000, costOfHotel: 2200 }
Explicación:
-
_.sum(_.map(data, key))
: genera la suma de cada matriz -
_.zipObject
:_.zipObject
los resultados con la suma de la matriz - el uso de
keys.map()
para la suma de cada clave como_.map
no garantiza el orden.
Documentación:
Usando for..in
para iterar el objeto y reduce
para iterar la matriz
var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];
var result = [data.reduce((acc, n) => {
for (var prop in n) {
if (acc.hasOwnProperty(prop)) acc[prop] += n[prop];
else acc[prop] = n[prop];
}
return acc;
}, {})]
console.log(result)
map
el objeto y calcular la suma y almacenarlo en otro.
var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];
var result = [];
var sum = 0;
var costsum = 0;
data.map(function(item, key){
var cost = item;
//nsole.log(cost);
sum = sum + cost.costOfAirtickets;
costsum = costsum + cost.costOfHotel;
});
result = [{costOfAirtickets:sum, costOfHotel:costsum}];
console.log(result);
Lodash
Usando lodash
reduce y _.mergeWith este es un liner:
var data = [{costOfAirtickets: 2500, costOfHotel: 1200}, {costOfAirtickets: 1500, costOfHotel: 1000}]
var result = _.reduce(data, (r,c) => _.mergeWith(r, c, (o = 0, s) => o + s), {})
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Solo ES6
Si NO desea mutar la matriz original, puede utilizar ES6 reduce , Object.entries y forEach esta forma:
var data = [{costOfAirtickets: 2500, costOfHotel: 1200}, {costOfAirtickets: 1500, costOfHotel: 1000}]
// One liner
var result1 = data.reduce((r, c) =>
!Object.entries(c).forEach(([key, value]) => r[key] = (r[key] || 0) + value) && r, {})
// More readable
var result2 = data.reduce((r, c) => {
Object.entries(c).forEach(([key, value]) => r[key] = (r[key] || 0) + value)
return r
}, {})
console.log(result1)
console.log(result2)
Si no nos importa mutar la matriz de data
inicial data
entonces podemos tener una solución de una línea:
var data = [{costOfAirtickets: 2500, costOfHotel: 1200}, {costOfAirtickets: 1500, costOfHotel: 1000}]
data.reduce((r, c) => !Object.entries(c).forEach(([key,value]) => r[key] += value) && r)
console.log(data[0])
Más legible:
var data = [{costOfAirtickets: 2500, costOfHotel: 1200}, {costOfAirtickets: 1500, costOfHotel: 1000}]
data.reduce((r, c) => {
Object.entries(c).forEach(([key, value]) => r[key] += value)
return r
})
console.log(data[0])
La única diferencia entre los ejemplos de mutación y no de mutación es el valor inicial para la reduce
(y también el hecho de que con la mutación usamos el índice 0 para acumular las sumas). En los mutantes no hay initial value
mientras que en los otros, comenzamos con el objeto literal vacío.
si necesita que el resultado sea una matriz específicamente, devuelva [data]
para los ejemplos de mutación y [result]
para los ejemplos puros.
var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}];
var result = [data.reduce((acc, n) => {
for (var prop in n) {
if (acc[prop]) acc[prop] += n[prop];
else acc[prop] = n[prop];
}
return acc;
}, {})]
console.log(result)