74

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?

2K01B5
  • 1,011
  • 1
  • 10
  • 23
  • 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;` – Jaromanda X Feb 16 '16 at 11:27
  • 1
    if all you want to do is "copy" the array ... `var stored = store.slice();` will do – Jaromanda X Feb 16 '16 at 11:30
  • To return a new array from an array, you want `map` not `reduce`. – Jamiec Feb 16 '16 at 11:37
  • 1
    @Andy care to elaborate? The [docs](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map) *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. – Jamiec Feb 16 '16 at 11:48
  • with .map, he'd do `var stored = store.map(function(v) { return v; });` ... which is simply `var stored = store.slice();` as mentioned above – Jaromanda X Feb 16 '16 at 11:52
  • Except `slice` gives you no ability to `console.log` each element, whare `map` does. – Jamiec Feb 16 '16 at 11:53
  • @Jamiec - ok ... `var stored = store.slice(); console.log(stored);` – Jaromanda X Feb 16 '16 at 11:53
  • Which will give a different result (in the console) to this. Keep arguing your non-point. – Jamiec Feb 16 '16 at 11:54
  • @Jamiec, `reduce` can return an array too. In fact it can return anything you want: a string, an object... – Andy Feb 16 '16 at 11:58
  • 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. – Jamiec Feb 16 '16 at 11:59
  • I was simply pointing out that your claim "To return a new array from an array, you want map not reduce" was incorrect. – Andy Feb 16 '16 at 12:00
  • 1
    @Andy - To bang a nail into a wall you want a hammer, not a shoe (Same point applies) – Jamiec Feb 16 '16 at 12:01
  • @JaromandaX Had the same problem, thanks for the concise and accurate answer. :) – jk121960 Sep 12 '18 at 15:15

6 Answers6

134

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.

Venkata Raju
  • 4,866
  • 3
  • 27
  • 36
39

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.

Jamiec
  • 133,658
  • 13
  • 134
  • 193
  • 5
    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). – dossy Nov 25 '20 at 15:23
  • I assume with ECMA it will be something like `store.map(it => {return it});`? – fedorqui Nov 30 '20 at 11:11
  • 1
    @fedorqui'SOstopharming' `store.map(it => ({it}))` will also work. – Jamiec Nov 30 '20 at 11:32
  • Excellent! While I think I would go with the version with a `return`, so it is easier to understand to me :) – fedorqui Nov 30 '20 at 11:34
23

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);
TJ 96
  • 318
  • 2
  • 8
4

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]
kmoser
  • 8,780
  • 3
  • 24
  • 40
abitofcode
  • 3,048
  • 1
  • 16
  • 11
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.

dfsq
  • 191,768
  • 25
  • 236
  • 258
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);
jean d'arme
  • 4,033
  • 6
  • 35
  • 70