web-dev-qa-db-fra.com

Javascript reduction () sur Object

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.

149
Pavel S.

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 });
269

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);
47
faboulaws

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);
18
ZER0

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
_
16
Daniel Bayley

É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 !! ;-)

4
user1247458

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}
3

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]]
2
Janus Troelsen

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 * /

1
kennebec

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
1
Chris Dolphin

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
);
1
Igor Sukharev

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);
    }
}
1
Francis Avila

Essayez cette fonction one-liner arrow

Object.values(o).map(a => a.value, o).reduce((ac, key, index, arr) => ac+=key)
0
M Thomas