5

Javascript code:

const bigArray2 = [
    [1, 2, 3],
    [2, 4, 6],
    [3, 6, 9],
  ];
  
  //creates single-layer array containing all elements of every array within bigArray2
  const combinedArray = bigArray2.reduce((accumulated, current) => {
    for (element of current) {
      accumulated.push(element);
    }
    return accumulated;
  });
  

  console.log(combinedArray);
  // outputs [ 1, 2, 3, 2, 4, 6, 3, 6, 9 ] as intended
  

  console.log(bigArray2); 
  // outputs [ [ 1, 2, 3, 2, 4, 6, 3, 6, 9 ], [ 2, 4, 6 ], [ 3, 6, 9 ] ]

Somehow bigArray2[0] is assigned a value of combinedArray. How and why? .reduce() is not supposed to change the original array

3 Answers 3

6

.reduce() is not supposed to change the original array

It is perfectly possible for .reduce() to change the original array; it is more about what you do within your callback that determines whether the original array is changed or not.

In your case, since you're omitting the second argument to .reduce() (ie: the initial value), .reduce() will first call your callback with an accumulator set to the first item in your array. In this case, that references your first array, ie: [1, 2, 3]. As a result, since you keep on returning this same array reference value from your .reduce() callback you end up with all the values being pushed to the first array.

To fix this, you can provide an empty array as an initial value, this way your callback will be initially called with the empty array as the accumulator, thus modifying that rather than your first element:

const bigArray2 = [ [1, 2, 3], [2, 4, 6], [3, 6, 9], ];

const combinedArray = bigArray2.reduce((accumulated, current) => {
  //   v--- declare this as const to avoid making it a global
  for (const element of current) {
    accumulated.push(element);
  }
  return accumulated;
}, []); // <--- pass an initial array

console.log(combinedArray);
// outputs [ 1, 2, 3, 2, 4, 6, 3, 6, 9 ] as intended


console.log(bigArray2); // left as is


As a side note, a task like this is better done with .flat():

const bigArray2 = [ [1, 2, 3], [2, 4, 6], [3, 6, 9], ];

const combinedArray = bigArray2.flat();
console.log(combinedArray);
// outputs [ 1, 2, 3, 2, 4, 6, 3, 6, 9 ] as intended
console.log(bigArray2); // left as is

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

3 Comments

Thank you very much for taking the time to answer so throughly and patiently. Good to know about .flat() (although this is from a codecademy exercise about .reduce()). I had noted that passing an initial array solved the issue but I didn't know why. Codecademy told me that .reduce() does not change the original and it states so on the W3 documentation: "The reduce() method does not change the original array."
But clearly that's given an initial array passed, and without it the accumulator refers to the same object. Lesson learnt, thank you.
@Jake Yeah, what those sources mean is that .reduce() typically returns something new, unlike a method like .sort() which modifies the existing array. It I wouldn’t say those are entirely accurate explanations of .reduce(). W3 Schools has a history of being incorrect, I do back Codecademy though (I originally learnt from there). The main docs to consult is MDN which is normally the most reliable (albeit can be a bit technical if you’re a beginner)
2

You're not passing an initial value to your reduce() function.

As noted on MDN:

If initialValue is not specified, accumulator is initialized to the first value in the array

When you then modify your accumulator, you're actually modifying the contents of the first element of your input array, in this case [1, 2, 3]. Note that the original array indeed does remain unchanged. After the reduce() operation, the original array still contains the same 3 elements, i.e. the same 3 references to its sub-arrays. It's just that by not passing an initial value, your logic ends up modifying the contents of the first of those sub-arrays.

To fix, initialize your accumulator with an empty array ([]):

const bigArray2 = [
  [1, 2, 3],
  [2, 4, 6],
  [3, 6, 9],
];

const combinedArray = bigArray2.reduce((accumulated, current) => {
  for (element of current) {
    accumulated.push(element);
  }
  return accumulated;
}, []);


console.log(combinedArray);
// outputs [ 1, 2, 3, 2, 4, 6, 3, 6, 9 ]


console.log(bigArray2);
// outputs [ [ 1, 2, 3 ], [ 2, 4, 6 ], [ 3, 6, 9 ] ]

2 Comments

Thank you. Codecademy and W3 documentation state that .reduce() does not change the original but clearly this is only in the case that an initial value is passed. I had noted that that fixed it and now I know why. Would you say it's best practise to always pass an initial value?
The documentation is correct in the sense that reduce() in itself does indeed not modify the original array. The original array remains unchanged, i.e. it still contains references to the same 3 sub-arrays. However, by not passing an initial value, your logic is effectively changing the contents of the first of those sub-arrays. The case where you want reduce() to actually operate on the first value of the input array is exceedingly rare, so I would indeed suggest always passing an initial value.
1
Array.prototype.reduce(callback, initialValue)

You simply didn't set an initial value (second argument) to .reduce. Therefore, the method automatically takes the first element as the accumulator (first argument) of the callback.

Refer to the docs

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.