Matt Y
Matt Y

Reputation: 68

Why can't we feed Array.prototype.concat directly into reduce?

JavaScript newbie here. Today I learned about reduce and set out to implement my own array flatten function.

What I had was

var array = [[1, 2], [3, 4, 5], [6]];
var result = array.reduce(Array.prototype.concat, []); // Causes Uncaught TypeError: Array.prototype.concat called on null or undefined
var result = array.reduce(Array.prototype.concat.call, []); // Causes Uncaught TypeError: undefined is not a function

While answers in Merge/flatten an array of arrays in JavaScript? are elegant and idiomatic, I'd really appreciate an illustration on how my attempts failed.

Upvotes: 4

Views: 1475

Answers (3)

Ry-
Ry-

Reputation: 225095

You have the right idea with Array.prototype.concat.call. With Array.prototype.concat, the calls are going to look like this:

var concat = Array.prototype.concat;

concat(concat(concat([],
                     [1, 2]),
              [3, 4, 5]),
       [6])

which doesn’t work because Array.prototype.concat concatenates its arguments to its this; calling it as concat() gives it a this of undefined. How about with call?

var call = Array.prototype.concat.call;

call(call(call([],
               [1, 2]),
          [3, 4, 5]),
     [6])

This runs into the same problem, but with Function.prototype.call (Array.prototype.concat is a function like any other, and inherits its call method from Function.prototype). call tries to call its this, but calling it as call() gives it a this of undefined.

You could pass Function.prototype.call.bind(Array.prototype.concat)… if reduce didn’t call its function with more arguments than just the accumulator and the current item. But it does, passing the index of the current item and context array as well, and ruining any chance of making this work by passing only Array.prototype.concat wrapped with some builtins.

Upvotes: 2

Soubhik Mondal
Soubhik Mondal

Reputation: 2666

This is so because Array.prototype.concat requires a function prototype such that it is an array or list of values to be concatenated to the main array.

However, the callback to Array.prototype.reduce requires an accumulator, currentValue, currentIndex and array as parameters.

The parameters (as well as what they're supposed to do) don't match and hence, you get unexpected results or errors.

Upvotes: 0

Jaromanda X
Jaromanda X

Reputation: 1

your code is equivalent of

result = array.reduce(fn, []);
function fn(a, b, index, array) { // this is the arguments reduce sends to the callback
    return Array.prototype.concat.call(null, a, b, index, array);
}

can you spot the problem?

Upvotes: 4

Related Questions