Tests fail when concatenating nested array in JavaScript

Implemented a Radix Sort which sorts a list of numbers. Here is my code:

function getDigit(number, index, lengthOfLongestNumber) {
  let numberString = number.toString();

  numberString = numberString.padStart(lengthOfLongestNumber, '0');

  return parseInt(numberString[index], 10) || 0;
}

function getLengthOfLongestNumber(numbers) {
  // return Math.floor(Math.log10(Math.max(...numbers))) + 1;

  let largestNumber = 0;

  for (let i = 0; i < numbers.length; i++) {
    if (numbers[i] > largestNumber) {
      largestNumber = numbers[i];
    }
  }

  return largestNumber.toString().length;
}

function radixSort(numbers) {
  const lengthOfLongestNumber = getLengthOfLongestNumber(numbers);

  for (let i = lengthOfLongestNumber - 1; i >= 0; i--) {
    const buckets = new Array(10).fill().map(() => []);

    while (numbers.length) {
      const number = numbers.shift();

      buckets[getDigit(number, i, lengthOfLongestNumber)].push(number);
    }

    for (let j = 0; j < 10; j++) {
      // numbers = numbers.concat(buckets[j]);  ---> uncomment this line and comment out the following while loop
      // numbers = [...numbers, ...buckets[j]]; ---> or uncomment this line and comment out the following while loop
      while (buckets[j].length) {
        numbers.push(buckets[j].shift());
      }
    }
  }

  return numbers;
}

Tests fail when I merge buckets array into numbers array using concat() in radixSort function (inside inner for loop). But it somehow passes if I use while loop instead.

You can see and run tests in CodeSandbox.

Why is this happening? What is the difference between them that causes tests fail?

Answer

For the returned array it doesn’t matter which of those alternatives you use, but if the tests expect you to sort the given array, so that after the call the input array has been sorted itself, then indeed, the commented-out alternatives will not work. This is because those alternatives assign to numbers instead of mutating it.

Just check the difference between the alternatives when ignoring the returned array, but looking at the given array:

let arr = [521, 219, 100, 422, 889, 529, 491, 777, 641, 882, 229];
radixSort(arr);
console.log(arr);

The commented-out alternatives will log an empty array.

The correct version mutates numbers, and does not assign a (new) array to it.

To avoid the loop, you can do this mutation also like this:

numbers.push(...buckets[j]);

And you can even replace the for loop around that statement, with just this line:

numbers.push(...buckets.flat());