266

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.

asked Apr 1, 2013 at 17:53
4
  • 1
    Are you using Underscore.js? Commented Apr 1, 2013 at 17:57
  • Nope. Does Underscore provide reduce for objects? Commented Apr 1, 2013 at 17:57
  • I can't remember. I know it has a reduce method. I would check there. Though, the solution doesn't seem that difficult. Commented Apr 1, 2013 at 17:58
  • 2
    @Sethen Maleno, @Pavel: yes _ does have a reduce for objects. Not sure if it works by accident or if object support was intentional, but indeed you can pass an object as in this question's example, and it will (conceptually) for..in, calling your iterator function with the values found at each key. Commented Apr 1, 2013 at 18:10

15 Answers 15

337

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 });
answered Apr 1, 2013 at 18:03
Sign up to request clarification or add additional context in comments.

5 Comments

Nice answer but it is more readable to use Object.values instead of Object.keys because we are concerned about the values here not the keys. It should be like this: Object.values(o).reduce((total, current) => total + current.value, 0);
Object.values has much worse browser support than Object.keys, but that might not be an issue if you use a polyfill or you transpile with Babel
exactly, i was using this in keys that has beet retrieved from mongo model, and i passed an empty object as an initial value to the result of reduce of keys, and as much as i was using @babel/plugin-proposal-object-rest-spread plugin , i was spread the accumulator value in return of reduce and it works like a charm, but i was wondere if im doing something wrong, be cause i was passing the obejct to the initial value of reduce and your answer proved me that i did the right thing!
But using Object.values doesn't give you access to the actual key being used. Using the key , particularly in reduction, is very common
also - outside of examples - who has a list of object properties that they want to simply sum up ? Most of the time object properties are distinct.
143

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
answered Aug 20, 2017 at 16:54

3 Comments

That's Array.prototype.values() that you linked to - edited now
doesn't reduce take a default value as a second parameter?
@danielrvt Yes but it’s optional, and in this case unnecessary. See here.
91

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);
answered Feb 27, 2017 at 14:58

8 Comments

Object.entries(o); // returns [['value',1],['value',2],['value',3]]
const [key, value] = pair; I have never seen this!
@martin-meeser - this is called destructuring. We can even omit this line by changing function (total, pair) to function (total, [key, value])
@faboulaws Object.entries(o); // returns [["a", { value: 1 }], ["b", { value: 2 }], ["c", { value: 3 }]]
@faboulaws your answer is wrong, the last line should be return total + value.value. becase Object.entries(o) [ [ "a", { "value": 1 } ], [ "b", { "value": 2 } ], [ "c", { "value": 3 } ] ] , it is very misleading. even 3 people thumbs up...
|
21

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);
answered Apr 1, 2013 at 18:16

Comments

6

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.

answered Oct 14, 2021 at 18:32

Comments

5

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}
answered May 9, 2016 at 14:50

Comments

4

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
);
answered Dec 27, 2018 at 12:21

Comments

3

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

answered Oct 5, 2014 at 11:43

4 Comments

-1, now you have a new enumerable property on all future objects: jsfiddle.net/ygonjooh
Please don't modify the base prototype like this. This can lead to a lot of problems for future developers working in the same code base
Yes, it is a "monkey patching" This solution was written 6 years ago and not too relevant now, just keep in mind And for example, it will be better to use Object.entries() in 2021
2

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]]
answered Apr 1, 2013 at 18:14

Comments

1

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

answered Apr 1, 2013 at 18:22

Comments

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);
 }
}
answered Apr 1, 2013 at 19:01

Comments

1

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)
answered Mar 14, 2020 at 8:30

Comments

1

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);
answered Jan 21, 2021 at 17:00

Comments

-1

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
answered Mar 1, 2018 at 0:54

3 Comments

You mean lodash?
Nope, I meant Underscore. But either works. They both have reduce()
Oops I never had heard of underscore thanks.
-1

Try out this one liner arrow function

Object.values(o).map(a => a.value, o).reduce((ac, key, index, arr) => ac+=key)
answered Jul 25, 2019 at 5:30

1 Comment

"Try this" answers are low-value on Stack Overflow because they miss an opportunity to empower/educate thousands of future researchers.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.