Je voudrais résumer les valeurs d'un objet.
Je suis habitué à python où ce serait juste:
sample = { 'a': 1 , 'b': 2 , 'c':3 };
summed = sum(sample.itervalues())
Le code suivant fonctionne, mais c'est beaucoup de code:
function obj_values(object) {
var results = [];
for (var property in object)
results.Push(object[property]);
return results;
}
function list_sum( list ){
return list.reduce(function(previousValue, currentValue, index, array){
return previousValue + currentValue;
});
}
function object_values_sum( obj ){
return list_sum(obj_values(obj));
}
var sample = { a: 1 , b: 2 , c:3 };
var summed = list_sum(obj_values(a));
var summed = object_values_sum(a)
Est-ce que je manque quelque chose d'évident, ou est-ce juste comme ça?
Vous pouvez tout mettre dans une fonction:
function sum( obj ) {
var sum = 0;
for( var el in obj ) {
if( obj.hasOwnProperty( el ) ) {
sum += parseFloat( obj[el] );
}
}
return sum;
}
var sample = { a: 1 , b: 2 , c:3 };
var summed = sum( sample );
console.log( "sum: "+summed );
Object.keys()
et Array.reduce()
(la prise en charge du navigateur ne devrait plus être un gros problème):function sum(obj) {
return Object.keys(obj).reduce((sum,key)=>sum+parseFloat(obj[key]||0),0);
}
let sample = { a: 1 , b: 2 , c:3 };
console.log(`sum:${sum(sample)}`);
Mais cela semble être beaucoup plus lent: jsperf.com
Cela peut être aussi simple que cela:
const sumValues = obj => Object.values(obj).reduce((a, b) => a + b);
Citant MDN:
La méthode
Object.values()
renvoie un tableau des valeurs de propriété énumérables propres à un objet donné, dans le même ordre que celui fourni par une bouclefor...in
(la différence étant qu'une boucle for-in énumère également des propriétés dans la chaîne de prototypes).
La méthode
reduce()
applique une fonction à un accumulateur et à chaque valeur du tableau (de gauche à droite) pour le réduire à une valeur unique.
de Array.prototype.reduce()
sur MDN
Vous pouvez utiliser cette fonction comme ça:
sumValues({a: 4, b: 6, c: -5, d: 0}); // gives 5
Notez que ce code utilise certaines fonctionnalités ECMAScript qui ne sont pas prises en charge par certains navigateurs plus anciens (comme IE). Vous devrez peut-être utiliser Babel pour compiler votre code.
Une boucle for
régulière est assez concise:
var total = 0;
for (var property in object) {
total += object[property];
}
Vous devrez peut-être ajouter object.hasOwnProperty
si vous avez modifié le prototype.
Si vous utilisez lodash, vous pouvez faire quelque chose comme:
_.sum(_.values({ 'a': 1 , 'b': 2 , 'c':3 }))
Une raison pour laquelle vous n'utilisez pas simplement une boucle for...in
?
var sample = { a: 1 , b: 2 , c:3 };
var summed = 0;
for (var key in sample) {
summed += sample[key];
};
Honnêtement, étant donné nos "temps modernes", j'adopterais une approche de programmation fonctionnelle chaque fois que possible, comme ceci:
const sumValues = (obj) => Object.keys(obj).reduce((acc, value) => acc + obj[value], 0);
Notre accumulateur acc
, commençant par la valeur 0, accumule toutes les valeurs en boucle de notre objet. Cela présente l’avantage supplémentaire de ne pas dépendre de variables internes ou externes; c'est une fonction constante pour ne pas être écrasé accidentellement ... gagnant pour ES2015!
Vous pouvez maintenant utiliser la fonction reduce
et obtenir la somme.
const object1 = { 'a': 1 , 'b': 2 , 'c':3 }
console.log(Object.values(object1).reduce((a, b) => a + b, 0));
Je suis un peu en retard pour le parti, cependant, si vous avez besoin d’une solution plus robuste et plus flexible, voici ma contribution. Si vous souhaitez additionner uniquement une propriété spécifique dans un combo objet/tableau imbriqué, ainsi que réaliser d'autres méthodes d'agrégat, voici une petite fonction que j'utilisais dans un projet React:
var aggregateProperty = function(obj, property, aggregate, shallow, depth) {
//return aggregated value of a specific property within an object (or array of objects..)
if ((typeof obj !== 'object' && typeof obj !== 'array') || !property) {
return;
}
obj = JSON.parse(JSON.stringify(obj)); //an ugly way of copying the data object instead of pointing to its reference (so the original data remains unaffected)
const validAggregates = [ 'sum', 'min', 'max', 'count' ];
aggregate = (validAggregates.indexOf(aggregate.toLowerCase()) !== -1 ? aggregate.toLowerCase() : 'sum'); //default to sum
//default to false (if true, only searches (n) levels deep ignoring deeply nested data)
if (shallow === true) {
shallow = 2;
} else if (isNaN(shallow) || shallow < 2) {
shallow = false;
}
if (isNaN(depth)) {
depth = 1; //how far down the rabbit hole have we travelled?
}
var value = ((aggregate == 'min' || aggregate == 'max') ? null : 0);
for (var prop in obj) {
if (!obj.hasOwnProperty(prop)) {
continue;
}
var propValue = obj[prop];
var nested = (typeof propValue === 'object' || typeof propValue === 'array');
if (nested) {
//the property is an object or an array
if (prop == property && aggregate == 'count') {
value++;
}
if (shallow === false || depth < shallow) {
propValue = aggregateProperty(propValue, property, aggregate, shallow, depth+1); //recursively aggregate nested objects and arrays
} else {
continue; //skip this property
}
}
//aggregate the properties value based on the selected aggregation method
if ((prop == property || nested) && propValue) {
switch(aggregate) {
case 'sum':
if (!isNaN(propValue)) {
value += propValue;
}
break;
case 'min':
if ((propValue < value) || !value) {
value = propValue;
}
break;
case 'max':
if ((propValue > value) || !value) {
value = propValue;
}
break;
case 'count':
if (propValue) {
if (nested) {
value += propValue;
} else {
value++;
}
}
break;
}
}
}
return value;
}
Il est récursif, non ES6, et il devrait fonctionner dans la plupart des navigateurs semi-modernes. Vous l'utilisez comme ceci:
const onlineCount = aggregateProperty(this.props.contacts, 'online', 'count');
Répartition des paramètres:
obj = un objet ou un tableau
property = la propriété des objets/tableaux imbriqués sur lesquels vous souhaitez exécuter la méthode d'agrégation
agrégé = la méthode d'agrégat (somme, min, max ou nombre)
shallow = peut être défini sur true/false ou sur une valeur numérique
depth = devrait être laissé nul ou indéfini (il est utilisé pour suivre les rappels récursifs suivants)
Shallow peut être utilisé pour améliorer les performances si vous savez que vous n'avez pas besoin de rechercher des données profondément imbriquées. Par exemple, si vous avez le tableau suivant:
[
{
id: 1,
otherData: { ... },
valueToBeTotaled: ?
},
{
id: 2,
otherData: { ... },
valueToBeTotaled: ?
},
{
id: 3,
otherData: { ... },
valueToBeTotaled: ?
},
...
]
Si vous souhaitez éviter de parcourir en boucle la propriété otherData, car la valeur que vous allez agréger n'est pas imbriquée aussi profondément, vous pouvez définir peu profond la valeur true.
A ramda one liner:
import {
compose,
sum,
values,
} from 'ramda'
export const sumValues = compose(sum, values);
Utilisez: const summed = sumValues({ 'a': 1 , 'b': 2 , 'c':3 });
Je suis tombé sur cette solution de @jbabey en essayant de résoudre un problème similaire. Avec un peu de modification, j'ai bien compris. Dans mon cas, les clés d’objet sont des nombres (489) et des chaînes ("489"). Par conséquent, pour résoudre ce problème, chaque clé est analysée. Le code suivant fonctionne:
var array = {"nR": 22, "nH": 7, "totB": "2761", "nSR": 16, "htRb": "91981"}
var parskey = 0;
for (var key in array) {
parskey = parseInt(array[key]);
sum += parskey;
};
return(sum);