Il existe une méthode Nice Array reduce()
pour obtenir une valeur du tableau. Exemple:
[0,1,2,3,4].reduce(function(previousValue, currentValue, index, array){
return previousValue + currentValue;
});
Quel est le meilleur moyen de réaliser la même chose avec des objets? J'aimerais faire ceci:
{
a: {value:1},
b: {value:2},
c: {value:3}
}.reduce(function(previous, current, index, array){
return previous.value + current.value;
});
Cependant, Object ne semble avoir aucune méthode reduce()
implémentée.
Une option serait de reduce
the keys()
:
var o = {
a: {value:1},
b: {value:2},
c: {value:3}
};
Object.keys(o).reduce(function (previous, key) {
return previous + o[key].value;
}, 0);
Avec cela, vous voudrez spécifier une valeur initiale ou le premier tour sera 'a' + 2
.
Si vous voulez que le résultat soit un objet ({ value: ... }
), vous devez initialiser et retourner l'objet à chaque fois:
Object.keys(o).reduce(function (previous, key) {
previous.value += o[key].value;
return previous;
}, { value: 0 });
Implémentation ES6: Object.entries ()
const o = {
a: {value: 1},
b: {value: 2},
c: {value: 3}
};
const total = Object.entries(o).reduce(function (total, pair) {
const [key, value] = pair;
return total + value;
}, 0);
Tout d’abord, vous n’obtenez pas tout à fait ce que la valeur précédente de réduire est.
Dans votre pseudo-code, vous avez return previous.value + current.value
, donc la valeur previous
sera un nombre lors du prochain appel, pas un objet.
Deuxièmement, reduce
est une méthode Array, pas celle d'un objet, et vous ne pouvez pas vous fier à l'ordre pour itérer les propriétés d'un objet (voir: https: //developer.mozilla .org/en-US/docs/JavaScript/Référence/Instructions/for ... in , cela s'applique à Object.keys aussi); Je ne suis donc pas sûr que l'application de reduce
sur un objet ait un sens.
Cependant, si la commande n'est pas importante, vous pouvez avoir:
Object.keys(obj).reduce(function(sum, key) {
return sum + obj[key].value;
}, 0);
Ou vous pouvez simplement map la valeur de l'objet:
Object.keys(obj).map(function(key) { return this[key].value }, obj).reduce(function (previous, current) {
return previous + current;
});
P.S. dans ES6 avec la syntaxe de la grosse flèche (déjà dans Firefox Nightly), vous pouvez réduire un peu:
Object.keys(obj).map(key => obj[key].value).reduce((previous, current) => previous + current);
Ce que vous voulez réellement dans ce cas, c'est Object.values
. Voici une implémentation concise ES6 dans cet esprit:
_const add = {
a: {value:1},
b: {value:2},
c: {value:3}
}
const total = Object.values(add).reduce((t, n) => t + n.value, 0)
console.log(total) // 6
_
ou simplement:
_const add = {
a: 1,
b: 2,
c: 3
}
const total = Object.values(add).reduce((t, n) => t + n)
console.log(total) // 6
_
Étendre Object.prototype.
Object.prototype.reduce = function( reduceCallback, initialValue ) {
var obj = this, keys = Object.keys( obj );
return keys.reduce( function( prevVal, item, idx, arr ) {
return reduceCallback( prevVal, item, obj[item], obj );
}, initialValue );
};
Exemple d'utilisation.
var dataset = {
key1 : 'value1',
key2 : 'value2',
key3 : 'value3'
};
function reduceFn( prevVal, key, val, obj ) {
return prevVal + key + ' : ' + val + '; ';
}
console.log( dataset.reduce( reduceFn, 'initialValue' ) );
'Output' == 'initialValue; key1 : value1; key2 : value2; key3 : value3; '.
n'joy il, les gars !! ;-)
1:
[{value:5}, {value:10}].reduce((previousValue, currentValue) => { return {value: previousValue.value + currentValue.value}})
>> Object {value: 15}
2:
[{value:5}, {value:10}].map(item => item.value).reduce((previousValue, currentValue) => {return previousValue + currentValue })
>> 15
3:
[{value:5}, {value:10}].reduce(function (previousValue, currentValue) {
return {value: previousValue.value + currentValue.value};
})
>> Object {value: 15}
Vous pouvez utiliser une expression de générateur (prise en charge dans tous les navigateurs depuis des années et dans Node) pour obtenir les paires clé-valeur dans une liste que vous pouvez réduire:
>>> a = {"b": 3}
Object { b=3}
>>> [[i, a[i]] for (i in a) if (a.hasOwnProperty(i))]
[["b", 3]]
Si vous pouvez utiliser un tableau, utilisez-le, la longueur et l’ordre d’un tableau valent la moitié de sa valeur.
function reducer(obj, fun, temp){
if(typeof fun=== 'function'){
if(temp== undefined) temp= '';
for(var p in obj){
if(obj.hasOwnProperty(p)){
temp= fun(obj[p], temp, p, obj);
}
}
}
return temp;
}
var O={a:{value:1},b:{value:2},c:{value:3}}
reducer(O, function(a, b){return a.value+b;},0);
/ * valeur renvoyée: (Nombre) 6 * /
Comme cela n’a pas encore été confirmé dans une réponse, le reduce
d’Underscore fonctionne également pour cela.
_.reduce({
a: {value:1},
b: {value:2},
c: {value:3}
}, function(prev, current){
//prev is either first object or total value
var total = prev.value || prev
return total + current.value
})
Remarque: _.reduce
renverra la seule valeur (objet ou autre) si l'objet de liste ne contient qu'un seul élément, sans appeler la fonction itérateur.
_.reduce({
a: {value:1}
}, function(prev, current){
//not called
})
//returns {value: 1} instead of 1
Bien qu'un objet puisse être transformé en représentation sous forme de tableau, avec les méthodes suivantes: Object.entries () , Object.keys () , Object.values () , et ensuite être réduit comme d'habitude tableau, je préférerais ne pas créer le tableau intermidiate, juste pour itérer sur un objet, pour économiser de la mémoire et des cycles!
J'ai donc créé une fonction d'assistance qui imite Array.prototype.reduce () de très près.
const {hasOwnProperty} = Object.prototype;
const reduce = (object, reducer, accumulator) => {
for (const key in object)
if (hasOwnProperty.call(object, key))
accumulator = reducer(accumulator, object[key], key, object);
return accumulator;
};
Vous pouvez également l'assigner à
Object.reduce = reduce;
comme cette méthode est très utile!
Donc, la réponse à votre question serait:
result = Object.reduce(
{
a: {value:1},
b: {value:2},
c: {value:3},
},
(accumulator, current) => (accumulator.value += current.value, accumulator), // reducer function must return accumulator
{value: 0} // initial accumulator value
);
Ce n’est pas très difficile à mettre en place vous-même:
function reduceObj(obj, callback, initial) {
"use strict";
var key, lastvalue, firstIteration = true;
if (typeof callback !== 'function') {
throw new TypeError(callback + 'is not a function');
}
if (arguments.length > 2) {
// initial value set
firstIteration = false;
lastvalue = initial;
}
for (key in obj) {
if (!obj.hasOwnProperty(key)) continue;
if (firstIteration)
firstIteration = false;
lastvalue = obj[key];
continue;
}
lastvalue = callback(lastvalue, obj[key], key, obj);
}
if (firstIteration) {
throw new TypeError('Reduce of empty object with no initial value');
}
return lastvalue;
}
En action:
var o = {a: {value:1}, b: {value:2}, c: {value:3}};
reduceObj(o, function(prev, curr) { prev.value += cur.value; return prev;}, {value:0});
reduceObj(o, function(prev, curr) { return {value: prev.value + curr.value};});
// both == { value: 6 };
reduceObj(o, function(prev, curr) { return prev + curr.value; }, 0);
// == 6
Vous pouvez également l'ajouter au prototype d'objet:
if (typeof Object.prototype.reduce !== 'function') {
Object.prototype.reduce = function(callback, initial) {
"use strict";
var args = Array.prototype.slice(arguments);
args.unshift(this);
return reduceObj.apply(null, args);
}
}
Essayez cette fonction one-liner arrow
Object.values(o).map(a => a.value, o).reduce((ac, key, index, arr) => ac+=key)