85

Why is it that when I want to use the push function inside the reduce function to return a new array I get an error. However, when I use the concat method inside the reduce function, it returns a new array with no problem.

All I'm trying to do is pass an array to the reduce function and return the same array.

var store = [0,1,2,3,4];

var stored = store.reduce(function(pV,cV,cI){
  console.log("pv: ", pV);
  return pV.push(cV);
},[]);

This returns an error. But when I use concat:

var store = [0,1,2,3,4];

var stored = store.reduce(function(pV,cV,cI){
  console.log("pv: ", pV);
  return pV.concat(cV);
},[]);

It returns the same array.

Any ideas why?

13
  • 6
    return PV.push means on the next iteration, PV will be a Number, not an array, because push returns the length of the array - if you want, you could do return pV.push(cV), pV; - though, there's no benefit except for 1 less line of code i.e. pV.push(cV); return pV; Commented Feb 16, 2016 at 11:27
  • 2
    if all you want to do is "copy" the array ... var stored = store.slice(); will do Commented Feb 16, 2016 at 11:30
  • 1
    @Andy care to elaborate? The docs literally state map calls a provided callback function once for each element in an array, in order, and constructs a new array from the results which seems to perfectly describe what the questioner was doing. Commented Feb 16, 2016 at 11:48
  • 1
    @Andy - you can bang a nail into a wall with the heel of your shoe - but generally we prefer the right tool for the right job! Read the docs to both methods, or just read my answer below. Commented Feb 16, 2016 at 11:59
  • 1
    I was simply pointing out that your claim "To return a new array from an array, you want map not reduce" was incorrect. Commented Feb 16, 2016 at 12:00

7 Answers 7

145

push returns the new length of the array.

What you need is the initially provided array.

So change the code as below.

var store = [0, 1, 2, 3, 4];

var stored = store.reduce(function(pV, cV, cI){
  console.log("pv: ", pV);
  pV.push(cV);
  return pV; // *********  Important ******
}, []);

concat returns the new array combining the elements of the provided array and concatenated elements. so it works.

Sign up to request clarification or add additional context in comments.

Comments

43

Just for completeness, and for the next person who happens on this question, what you're doing is typically achieved with map which, as stated in the docs

map calls a provided callback function once for each element in an array, in order, and constructs a new array from the results

Contrast that with the description of reduce:

The reduce() method applies a function against an accumulator and each value of the array (from left-to-right) to reduce it to a single value.

(Emphasis mine) So you see, although you can manipulate reduce to return a new array, it's general usage is to reduce an array to a single value.

So for your code this would be:

var store = [0,1,2,3,4];

var stored = store.map(function(pV){
  console.log("pv: ", pV);
  return pV;
});

Much simpler than trying to reconstruct a new array using either push or concat within a reduce function.

4 Comments

Sometimes, you want to do a .filter().map() and rather than being O(2N) you can cut the work in half by doing a single pass using .reduce(), keeping yourself O(N).
I assume with ECMA it will be something like store.map(it => {return it});?
@fedorqui'SOstopharming' store.map(it => ({it})) will also work.
Excellent! While I think I would go with the version with a return, so it is easier to understand to me :)
26

I know this is the same answer, but I just want to show that using reduce (), the syntax can also be reduced to a single line of code using ES6:

var store = [0,1,2,3,4];

var stored = store.reduce((pV,cV) => [...pV, cV], []);

console.log(stored);

1 Comment

or var stored = store.reduce((pV,cV) => (pV.push(cV), pV), []);
5

reduce() can be useful if you need to return an array with multiple items for each item iterated:

var inputs = media.reduce((passedArray, video) => {
    passedArray.push("-i");
    passedArray.push(video.filepath);
    return passedArray;
}, []);

Here it's being used to build the input array for FFmpeg;

[{ name: "bob", filepath: "1.mp4" }, { name: "sue", filepath: "3.mp4" }]
=> ["-i", "1.mp4", "-i", "2.mp4]

Comments

2

Array.prototype.push method returns the new length of the array.

Array.prototype.concat method inserts new element into array and returns array back so it can be further processed. This is what you need to do with reduce: pass modified array the the next iteration.

1 Comment

But it's not the same array; concat allocates a new array which is inefficient.
2

You can always use destructuring:

var store = [0,1,2,3,4];

var stored = store.reduce(function(pV,cV,cI){
  console.log("pv: ", pV);
  return [...pV, cV];
},[]);

console.log(stored);

Comments

0

Here is a one-liner that uses the push() method with a comma expression:

let store = [0,1,2,3,4];

let stored = store.reduce((pV,cV) => (pV.push(cV), pV), []);

console.log(stored);

Comments

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.