8

I'm not getting expected results when trying this reduce in JavaScript:

let x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];

x.reduce((a,b) => a.length + b.length, []);

Simple, right? Here's what I expect to happen, step-by-step:

----------------------------------------------------------------
| callback | a (accumulator) | b (currentValue) | return value |
----------------------------------------------------------------
| 1st run  | 0               | 3                | 3            |
----------------------------------------------------------------
| 2nd run  | 3               | 3                | 6            |
----------------------------------------------------------------
| 2nd run  | 6               | 3                | 9            |
----------------------------------------------------------------

What do I actually get? NaN. In english, if I understand things correctly, the iterator first looks at the initial value I give it (the empty array as the 2nd argument to reduce) and that is represented by the argument a. My code shows a simple .length on both arguments being added. Where am I going wrong here?

6
  • 1
    The inputs are arrays, not numbers, you're glossing over this in your chart. a's first value is [], not 0, so a.length works. By returning a number, you're feeding a number into the next iteration, and invoking .length on it, which is obviously not your intention. Commented Jan 2, 2017 at 4:10
  • @meagar Yup, just realized that looking at Pointy's code below. Thank you! Commented Jan 2, 2017 at 4:13
  • 1
    If your intent is to sum the length of the arrays, you can map and reduce very cleanly: x.map(a => a.length).reduce((a,b) => a + b) Commented Jan 2, 2017 at 4:13
  • Walk though your code with a debugger, examining variables at each step. Commented Jan 2, 2017 at 4:43
  • @meager Your code will fail with an empty array--which one would expect to return zero. Commented Jan 2, 2017 at 4:44

4 Answers 4

18

I think this is what you're trying to do (variables renamed for clarity):

let x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];

x.reduce((acc,element) => acc + element.length, 0);

This is how it would happen step-by-step:

--------------------------------------------------------------
| callback | acc | element   | element.length | return value |
--------------------------------------------------------------
| 1st run  | 0   | [1, 2, 3] | 3              | 3            |
--------------------------------------------------------------
| 2nd run  | 3   | [4, 5, 6] | 3              | 6            |
--------------------------------------------------------------
| 3rd run  | 6   | [7, 8, 9] | 3              | 9            |
--------------------------------------------------------------
Sign up to request clarification or add additional context in comments.

Comments

8

The .reduce() API expects that your callback function will return the accumulator value (in your case, the value that starts off as []). Your callback just returns a value.

To make your code work, you'd need something like:

x.reduce((a,b) => { a.push(a.length + b.length); return a; }, []);

Now, if what you really want is a sum of the lengths of the arrays, then you don't want to accumulate into an array anyway; you want a simple sum. Instead of having the accumulator be an array then it just needs to be a number:

var sum = x.reduce(((sum, array) => sum + array.length), 0);

Start with zero, and each call to the function adds the array length to the sum.

6 Comments

@TheQodesmith just did :)
Ahh, I see. So in my case, since I'm accumulating array lengths, an array would need to be returned to continue iterating to be consistent.
Right. The .reduce() function requires that your callback returns the accumulator.
Just an FYI, that code won't actually return the complete length, it returns an array. But you got me to a working solution below. Thanks again.
But he wants to get 9. this returns [3,4,5] which I don't think means anything at all.
|
2

As an alternative to using reduce, flatten the array and take the length of the result.

function flatten(a) { return [].concat(...a); }

const x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
const length = flatten(x).length;

console.log(length);

The implementation of flatten above is for arrays which have depth of one or two. Extend/replace it as necessary for more deeply nested arrays.

Comments

1

Pointy's code above (doesn't actually return 9 like intended) got me to this working solution:

let x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
x.reduce((a,b) => a + b.length, 0);

Meager also pointed out in the comments that this is a classic case for the map/reduce combo as such (a much sleeker solution):

let x = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
x.map(a => a.length).reduce((a,b) => a + b);

3 Comments

a will never be array in reduce when you set the initial start value as zero and never return array from the callback See developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Meager's solution is flawed, because it is missing an initial value and therefore fails if the array has no elements, even though that probably should be valid input returning zero.
@torazaburo I put the variable assignment in meager's solution to at least show its for this particular instance. But point taken.

Your Answer

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.