[javascript] Javascript reduce() on Object

There is nice Array method reduce() to get one value from the Array. Example:

[0,1,2,3,4].reduce(function(previousValue, currentValue, index, array){
  return previousValue + currentValue;
});

What is the best way to achieve the same with objects? I'd like to do this:

{ 
    a: {value:1}, 
    b: {value:2}, 
    c: {value:3} 
}.reduce(function(previous, current, index, array){
  return previous.value + current.value;
});

However, Object does not seem to have any reduce() method implemented.

This question is related to javascript arrays object reduce

The answer is


If you can use an array, do use an array, the length and order of an array are half its worth.

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

/* returned value: (Number) 6 */


You can use a generator expression (supported in all browsers for years now, and in Node) to get the key-value pairs in a list you can reduce on:

>>> a = {"b": 3}
Object { b=3}

>>> [[i, a[i]] for (i in a) if (a.hasOwnProperty(i))]
[["b", 3]]

If handled as an array is much easier

Return the total amount of fruits:

let fruits = [{ name: 'banana', id: 0, quantity: 9 }, { name: 'strawberry', id: 1, quantity: 1 }, { name: 'kiwi', id: 2, quantity: 2 }, { name: 'apple', id: 3, quantity: 4 }]

let total = fruits.reduce((sum, f) => sum + f.quantity, 0);

One option would be to 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);

With this, you'll want to specify an initial value or the 1st round will be 'a' + 2.

If you want the result as an Object ({ value: ... }), you'll have to initialize and return the object each time:

Object.keys(o).reduce(function (previous, key) {
    previous.value += o[key].value;
    return previous;
}, { value: 0 });

Since it hasnt really been confirmed in an answer yet, Underscore's reduce also works for this.

_.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
})

Note, _.reduce will return the only value (object or otherwise) if the list object only has one item, without calling iterator function.

_.reduce({ 
    a: {value:1} 
}, function(prev, current){
    //not called
})

//returns {value: 1} instead of 1

This is not very difficult to implement yourself:

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

In 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

You can also add it to the Object prototype:

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

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

Sample of using.

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 it, guys!! ;-)


Try out this one liner arrow function

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

An object can be turned into an array with: Object.entries(), Object.keys(), Object.values(), and then be reduced as array. But you can also reduce an object without creating the intermediate array.

I've created a little helper library odict for working with objects.

npm install --save odict

It has reduce function that works very much like Array.prototype.reduce():

export const reduce = (dict, reducer, accumulator) => {
  for (const key in dict)
    accumulator = reducer(accumulator, dict[key], key, dict);
  return accumulator;
};

You could also assign it to:

Object.reduce = reduce;

as this method is very useful!

So the answer to your question would be:

const 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
);

ES6 implementation: 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);

First of all, you don't quite get what's reduce's previous value is.

In you pseudo code you have return previous.value + current.value, therefore the previous value will be a number on the next call, not an object.

Second, reduce is an Array method, not an Object's one, and you can't rely on the order when you're iterating the properties of an object (see: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Statements/for...in, this is applied to Object.keys too); so I'm not sure if applying reduce over an object makes sense.

However, if the order is not important, you can have:

Object.keys(obj).reduce(function(sum, key) {
    return sum + obj[key].value;
}, 0);

Or you can just map the object's value:

Object.keys(obj).map(function(key) { return this[key].value }, obj).reduce(function (previous, current) {
    return previous + current;
});

P.S. in ES6 with the fat arrow function's syntax (already in Firefox Nightly), you could shrink a bit:

Object.keys(obj).map(key => obj[key].value).reduce((previous, current) => previous + current);

Try this one. It will sort numbers from other variables.

const obj = {
   a: 1,
   b: 2,
   c: 3
};
const result = Object.keys(obj)
.reduce((acc, rec) => typeof obj[rec] === "number" ? acc.concat([obj[rec]]) : acc, [])
.reduce((acc, rec) => acc + rec)

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}

Examples related to javascript

need to add a class to an element How to make a variable accessible outside a function? Hide Signs that Meteor.js was Used How to create a showdown.js markdown extension Please help me convert this script to a simple image slider Highlight Anchor Links when user manually scrolls? Summing radio input values How to execute an action before close metro app WinJS javascript, for loop defines a dynamic variable name Getting all files in directory with ajax

Examples related to arrays

PHP array value passes to next row Use NSInteger as array index How do I show a message in the foreach loop? Objects are not valid as a React child. If you meant to render a collection of children, use an array instead Iterating over arrays in Python 3 Best way to "push" into C# array Sort Array of object by object field in Angular 6 Checking for duplicate strings in JavaScript array what does numpy ndarray shape do? How to round a numpy array?

Examples related to object

How to update an "array of objects" with Firestore? how to remove json object key and value.? Cast object to interface in TypeScript Angular 4 default radio button checked by default How to use Object.values with typescript? How to map an array of objects in React How to group an array of objects by key push object into array Add property to an array of objects access key and value of object using *ngFor

Examples related to reduce

Javascript reduce() on Object How to use filter, map, and reduce in Python 3 What is the 'pythonic' equivalent to the 'fold' function from functional programming? Finding the average of a list NameError: name 'reduce' is not defined in Python Javascript reduce on array of objects