Reduce() questions

Reduce() questions
0

#1

Hello! I want to add the values up in an array of objects, where the object keys differ, as in,

var foo = [{a:1},{b:2},{c:3}];

I know how to aggregate/reduce when keys are the same, i.e,

var foo = [{a:1},{a:2},{a:3}];
foo.reduce((a,c => a + c.a,0) // 6

I have tried the following,

var foo = [{a:1},{b:2},{c:3}];
foo.reduce((acc,cur) =>{
  acc.total += cur[1];
  return acc;
},{total: 0})

But the result is NaN. Can you explain my error/help? Thanks.


#2

I’ve edited your post for readability. When you enter a code block into a forum post, please precede it with a separate line of three backticks and follow it with a separate line of three backticks to make easier to read.

See this post to find the backtick on your keyboard. The “preformatted text” tool in the editor (</>) will also add backticks around text.

Note: Backticks are not single quotes.

In the last code, you need to think about what cur is. cur is an object. When you refer to cur[1], there must be a property named “1” in the object, otherwise it evaluates to undefined. Basically each iteration of the reduce is attempting to add undefined to acc.total, which is why you get NaN.


#3

Right so cur is an object, and I want to get at the value of the object but the key changes, so I can’t just call all the values by a single key…and that’s where I’m stuck :frowning:


#4

Read about the Object.keys method. This is what I would use to solve this problem using reduce.

You could also use Object.values instead.


#5

For cur[1] to work, there would have to be a property with the key 1 on the object, so that can’t work.

So this will work:

Object.values(curr)[0]

So you are getting an array containing the values of the object, and just picking the first one (I am assuming all your objects have a single key).

Note this is incredibly fragile. This is ok for a toy example, but in reality, if your data is a list of objects, and you don’t know the keys for the values you want from those objects, then your data is probably gibberish and unusable. An object is a keyed map of data: if you have no idea what that map looks like then that’s a problem


#6

Can the objects have more than one property/value pair? Are the values of the properties always numbers? If you know for sure the values are all numbers, then you could do the following which would even allow for multiple properties in each object:

var foo = [{a:1},{b:2},{c:3}];
foo.reduce((acc, obj) => acc += Object.values(obj).reduce((sum, val) => sum + val), 0); // 6

#7

Thanks for that Dan.
I’m trying to work my way up to a more complicated application of reduce I came across in the ‘Cash register challenge’ that looks like this,

 var register = cid.reduce(function(acc, curr) {
    acc.total += curr[1];
    acc[curr[0]] = curr[1];
    return acc;
  }, { total: 0 });

This code sums up the dollar values in the cid object, passed as an argument, but I don’t understand how, so I am trying to build up my comprehension with simpler exercises like

var foo = [{a:1},{b:2},{c:3}]

But I am totally flailing.


#8

Hmmm. It’s all blurry?


#9

You can click on it to view.


#10

Ah right, this makes more sense given the challenge. The question then is: why are your elements in an object?

The challenge gives you them in a form that’s easy to reduce (an array of two-element arrays - a map). At no point should you need to put the actual change in an object, particularly as the challenge expects the same structure as the input for the change key on the output.


#11

Oh cool. Ok that gives me a toe-hold, thank you very much. So the first reduce extracts the values from each object, puts them into an array, and the second reduce then acts on a simple array to add up the values…brilliant.


#12

Well that’s true, I kind of got away from the original problem and went down a reduce rabbit hole. Then I started making myself a reduce cheat sheet and got stumped by data structured as

var foo = [{a:1},{b:2},{c:3}];

Thanks for bearing with me.


#13

So that data structure isn’t really a reasonable data structure, so it’s likely not something you need to worry about too much. The reason it’s not reasonable is easier to understand if you think about how you store data.

  1. if you have an object {a: 1, b: 2, c: 3}, that’s reasonable. It’s like one instance of a thing - like a user: { name: 'Foo', age: 25 }. You wouldn’t reduce etc over it because it’s an object, you just look up what you want on it (user.name).
  2. if you have a map of data, that’s reasonable as well. By map, I mean something that looks like this: [[key, value],[key, value]] in JS*.
  3. if you have a list of objects, and those objects all have the same type, the same shape, then that’s reasonable. It’s a collection of things of a type - users for example. You would totally map/reduce/etc over that - get all the ages of users, get the average age of users, whatever.
  4. If you have a list of values, that’s reasonable: it’s just a plain list of values from somewhere.

What you’ve ended up at, how would that be stored? It can’t have come out of a relational database because 3 is what you get out when you want a list of items: if you ask for a collection of users, you don’t get {name: 'Foo' } as the first item in the collection, then {age: 21} for the second item in the collection.

It doesn’t quite make sense, a list of objects with unknown keys, because the end result is that you’re guessing things, and computers aren’t very good at doing that.

* What you have in the challenge is a map, and it wouldn’t be uncommon to have unknown keys. But with a map you’re likely to have a known type/format of key or value or both. For example a web cache is normally stored with the url as the key and the page HTML as the value.


#14

That really makes perfect sense. I can see now that data structured as

[{a: 1},{b:2}];

wouldn’t have much real world application…I kind of got carried away trying to reduce everything in sight. Thanks for pointing that out.