Running into Stupid Mistake with Array.prototype.fill() in JavaScript


Today I was picking up a previously abandoned LeetCode problem, the Sudoku Solver, and decided to try solving it using JavaScript since I had been writing Node.JS recently.
In my previous solution with Java, I was able to quickly get through the starting stage:

Categorize pre-filled values in a given Sudoku matrix into three 9-slot arrays (each array representing 9 slots for a row, a coloumn, or a 3x3 block respectively). It is worth noting that each slot also has places for holding key-value pairs denoting the existence of 1 to 9.

where I used two sets of nested for loop in Java to do the job: one set for initializing the 9 values inside each slot to 0 s, and another set for filling in the slots based on the given Sudoku matrix.

When trying to complete a similar task in JavaScript today, however, I introduced a seemingly clever trick, which eventually turned out to be a stupid mistake, and kept me from proceeding for over an hour.

The story began with Array.prototype.fill()

As has been mentioned, the input of this Sudoku problem was a 2D array (9 x 9), and as a normal thought, I attempted to categorize its pre-filled values into their respective rows, columns, and 3 x 3 blocks.

First things first, I declared my variables:

let slots = [new Array(9), new Array(9), new Array(9)];

So far so good.

Next, initialize slots. Normally you would (and should) use the oldschool (yet proven to be ALWAYS reliable) for loop approach for initializatoin. Contrary to this, I tried to be smart, and decided to give a shot on Array.prototype.fill(), a built-in function similar to the Arrays.fill() static method in Java’s Arrays class which fills repeating values into an array, for filling in the slots (each {} object to be assigned properties named '1' - '9' and '.' respectively):


My actions immediately brought a disaster whose cause I had not figured out until more than an hour later.

After having initialized my slots, I went ahead to do the categorizing of the given Sudoku matrix:

for (i = 0; i < 9; i++) {
    for (j = 0; j < 9; j++) {
        slots[0][i][board[i][j]] = true;
        slots[1][j][board[i][j]] = true;
        slots[2][3 * Math.floor(i / 3) + Math.floor(j / 3)][board[i][j]] = true;

Just to make sure it worked as I was expecting (which did not), I used console.log() to test my freshly filled-in slots on the example input


and, to my astonishment, got the following output, every slot was already full with properties naming '1' - '9' and '.':

Having completely no idea why this happened, I stared at my code, modifying the categorizing for loop effortlessly, with the puzzling result remaining as ever. Here is what I was actually expecting:

It was after a relentless fight that I finally looked into the way I initialized my slots. I went to check the documentation of Array.prototype.fill() on Mozilla’s website, and in the Description section, there is this sentence:

When fill gets passed an object, it will copy the reference and fill the array with references to that object.

So that was what caused this aftermath!
Basically, the “clever” way I initialized my slots was assigning references to the same empty object ({}) within each row, so my categorization process was not actually separating the pre-filled values into 27 different slots, but rather putting them into three single slots (one slot each row). How stupid.

For loop sweet for loop

In the end, I swithced my slots initialization process back to using a for loop, and everything went smoothly onwards as expected:

for (i = 0; i < 3; i++) {
    for (j = 0; j < 9; j++) {
        slots[i][j] = {};

As it turned out, for loop rocks!

Analogy to Java’s Arrays.fill()

I deemed it worth noting that this scenario also applies to Java’s Arrays.fill() and similar functionalities of other pass-by-reference programming languages, as they would also be passing unchanging references to given variables between their parentheses of fill().