J'ai un tableau d'objets qui ressemble à ceci:
var data = [{costOfAirtickets: 2500, costOfHotel: 1200},{costOfAirtickets: 1500, costOfHotel: 1000}]
et je veux résumer chaque élément du tableau pour produire un tableau comme celui-ci:
var result = [{costOfAirtickets: 4000, costOfHotel: 3200}]
J'ai utilisé une carte et une fonction de réduction, mais je n'ai pu résumer qu'un élément individuel comme suit:
data.map(item => ite.costOfAirtickets).reduce((prev, next)=>prev + next); // 22
Pour le moment, cela produit une valeur unique qui n’est pas ce que je veux comme explication initiale.
Est-il possible de le faire en Javascript ou probablement avec lodash.
Utiliser for..in
pour itérer un objet et reduce
pour itérer un tableau
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)
Utilisez Lodash pour vous simplifier la vie.
const _ = require('lodash')
let keys = ['costOfAirtickets', 'costOfHotel'];
let results = _.zipObject(keys, keys.map(key => _.sum( _.map(data, key))))
...
{ costOfAirtickets: 4000, costOfHotel: 2200 }
Explication:
_.sum(_.map(data, key))
: génère la somme de chaque tableau_.zipObject
: compresse les résultats avec la somme du tableaukeys.map()
pour la somme de chaque clé en tant que _.map
ne garantit pas l'ordre.Documentation:
Vous n'avez pas besoin de mapper le tableau, vous ne faites que réduire les éléments qu'il contient.
const totalCostOfAirTickets: data.reduce((prev, next) => prev + next.costOfAirTickets, 0)
const totalCostOfHotel: data.reduce((prev, next) => prev + next.costOfHotel, 0)
const totals = [{totalCostOfAirTickets, totalCostOfHotel}]
En une fois, vous pourriez faire quelque chose comme
const totals = data.reduce((prev, next) => {
prev.costOfAirTickets += next.costOfAirTickets;
prev.costOfHotel += next.costOfHotel;
}, {costOfAirTickets: 0, costOfHotel: 0})
Une autre solution consisterait à utiliser Map
(pas Array.prototype.map) car il présente plusieurs différences notables par rapport aux objets :
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())
La première ci-dessus itère sur votre tableau data
en utilisant la méthode reduce
. Chaque objet à l'intérieur de celui-ci sera appelé un enregistrement - distingué dans le code via la variable, rcd
.
Chaque itération de réduction renvoie une valeur qui est transmise comme premier argument à la prochaine itération de la boucle. Dans ce cas, le paramètre collection
contient cet argument, qui correspond à votre ensemble de sommes.
Dans la boucle reduce
, chaque paire clé/valeur de l'enregistrement est itérée à l'aide de forEach
. Pour obtenir le couple clé/valeur, la méthode Object.entries
est utilisée. À l'aide du tableau de déstructuration, ces arguments peuvent être directement affectés aux variables respectives, key
et value
.
Contrairement à un objet primitif, Map
a ses propres méthodes pour obtenir et définir ses entrées à l'aide de get()
et set()
. Donc, récupérez d'abord la somme précédente en utilisant get()
; si elle n'est pas définie, définissez par défaut sur 0
, comme le fait le || 0
. À ce stade, vous pouvez supposer que la somme précédente est au moins égale à 0 ou supérieure et y ajouter la valeur de la clé actuelle.
Si vous trouvez que Map est un peu lourd, vous pouvez également utiliser un objet similaire, tel que Set , qui utilise plusieurs des mêmes méthodes (à l'exception de get()
), ou vous pouvez également utiliser une primitive. objet (ie {}
).
Voici une approche lodash
_(data).flatMap(_.entries).groupBy(0).mapValues(v=>_.sumBy(v, 1)).value()
Il va résumer par toutes les clés uniques.
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>
Enveloppez votre résultat dans un [...]
ou utilisez un .castArray()
à la fin avant de décompresser en utilisant .value()
au cas où vous voudriez un tableau comme résultat.
Pour créer une valeur résultante/réduite, vous devez utiliser la méthode .reduce()
à la place 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);
Ma réponse simpliste serait comme;
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);
Remarque sur l'utilisation de .reduce()
: Si les éléments du tableau et la valeur accumulée sont du même type, vous pouvez envisager de ne pas utiliser une valeur initiale comme accumulateur et de réduire les éléments précédents (p
) et actuels (c
). La réduction externe dans l'extrait ci-dessus est de ce type. Toutefois, la réduction interne prend un tableau de paires clé (k
) (v
) sous la forme [k,v]
pour renvoyer un objet; par conséquent, une valeur initiale (p
) est essentielle.
Le résultat est la valeur accumulée en tant qu'objet. Si vous en avez besoin dans un tableau, placez-le simplement dans un tableau comme [res]
.
map
l'objet et calculer la somme et le stocker dans un autre.
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);
Vous pouvez utiliser une simple boucle forEach()
pour cela:
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);
Essayez d'utiliser réduire seulement comme dans l'extrait ci-dessous. Essayez d'éviter plusieurs itérations. J'espère que l'extrait ci-dessous aide!
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)
Vous pouvez réduire le tableau en prenant un objet pour la somme.
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);
C’est l’approche la plus facile à laquelle je puisse penser
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);
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)
Lodash
Utiliser lodash
réduire et _.mergeWith _ ceci est un one-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>
ES6 uniquement
Si vous ne voulez PAS transformer le tableau d'origine, vous pouvez utiliser ES6 réduire , Object.entries et pourEach comme ceci:
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 nous ne nous soucions pas de mutating le tableau initial data
, nous pouvons alors avoir une solution one-liner:
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])
Plus lisible:
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 seule différence entre les exemples en mutation et ceux en mutation est la valeur initiale pour le reduce
(et aussi le fait qu'avec la mutation, nous utilisons l'indice 0 comme un accumulateur pour les sommes). Dans les mutants, il n'y a pas de initial value
; dans les autres, on commence par un objet littéral vide.
si vous voulez que le résultat soit un tableau spécifique, renvoyez [data]
pour les exemples en mutation et [result]
pour les exemples purs.