Javascript reduce() on Object

JavascriptArraysObjectReduce

Javascript Problem Overview


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.

Javascript Solutions


Solution 1 - Javascript

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

Solution 2 - Javascript

What you actually want in this case are the Object.values. Here is a concise ES6 implementation with that in mind:

const add = {
  a: {value:1},
  b: {value:2},
  c: {value:3}
}

const total = Object.values(add).reduce((t, {value}) => t + value, 0)

console.log(total) // 6

or simply:

const add = {
  a: 1,
  b: 2,
  c: 3
}

const total = Object.values(add).reduce((t, n) => t + n)

console.log(total) // 6

Solution 3 - Javascript

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.value;
}, 0);

Solution 4 - Javascript

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

Solution 5 - Javascript

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

Solution 6 - Javascript

Let me summarise the possibilities. The aim is always to make an array out of the object. There are various Javascript object functions for this. For each individual function, there are different ways of interpreting it. So it always depends on what our object looks like and what we want to do.

In the example above, it is an object with three objects.

const obj = { 
    a: {value: 1}, 
    b: {value: 2}, 
    c: {value:3} 
};
With Object.keys

Object.keys only gives us the keys of the object.

const arr = Object.keys(obj);
// output arr: 
[a, b, c]

const result = arr.reduce((total, key) => {
    return sum + obj[key].value;
}, 0);
// output result
// 6
With Object.value

Object.value() returns the every single value in an array.

const arr = Object.value(obj);
// output arr
[
   {value: 1},
   {value: 2},
   {value: 3},
]

const result = arr.reduce((total, singleValue) => {
   return total + singleValue.value;
}, 0);

// output result
// 6

// Or the short variant
const resultShort = Object.values(obj).reduce((t, n) => t + n.value, 0)

// output resultShort
// 6
With Object.entries

Object.entries splits each individual object value into an array.

const arr = Object.entries(obj)
// output arr
[
  ["a", {visitors: 1}],
  ["b", {visitors: 2}],
  ["c", {visitors: 4}]
]

const result = arr.reduce((total, singleArr) => {
  return total + singleArr[1].value;
}, 0);

// output result
// 6

Whether you do it with reduce or with the array function map() depends on you and what you want to do.

Solution 7 - Javascript

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

Solution 8 - Javascript

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}

Solution 9 - Javascript

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]]

Solution 10 - Javascript

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

Solution 11 - Javascript

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

Solution 12 - Javascript

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)

Solution 13 - Javascript

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

Solution 14 - Javascript

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

Solution 15 - Javascript

Try out this one liner arrow function

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPavel S.View Question on Stackoverflow
Solution 1 - JavascriptJonathan LonowskiView Answer on Stackoverflow
Solution 2 - JavascriptDaniel BayleyView Answer on Stackoverflow
Solution 3 - JavascriptfaboulawsView Answer on Stackoverflow
Solution 4 - JavascriptZER0View Answer on Stackoverflow
Solution 5 - JavascriptIgor SukharevView Answer on Stackoverflow
Solution 6 - JavascriptMaik LowreyView Answer on Stackoverflow
Solution 7 - Javascriptuser1247458View Answer on Stackoverflow
Solution 8 - JavascriptAlair Tavares JrView Answer on Stackoverflow
Solution 9 - JavascriptJanus TroelsenView Answer on Stackoverflow
Solution 10 - JavascriptkennebecView Answer on Stackoverflow
Solution 11 - JavascriptFrancis AvilaView Answer on Stackoverflow
Solution 12 - JavascriptprgvView Answer on Stackoverflow
Solution 13 - JavascriptAlfredo BollatiView Answer on Stackoverflow
Solution 14 - JavascriptChris DolphinView Answer on Stackoverflow
Solution 15 - JavascriptM ThomasView Answer on Stackoverflow