Sunday, February 19, 2023

Working with 2D Arrays in JavaScript

Working with 2D Arrays in JavaScript

If you already understand the basics of JavaScript arrays, it's time to take your skills to the next level with more advanced topics. In this series of tutorials, you'll explore intermediate-level topics for programming with arrays in JavaScript.

In this tutorial, you'll learn how to work with 2D arrays in JavaScript. JavaScript doesn't have built-in support for 2D arrays. This is not a big issue because arrays in JavaScript can contain other arrays as their elements. This means that we can create 2D arrays in JavaScript by creating an array of arrays.

Let's review some of the basics such as 2D array initialization and then implement additional functionality like getting and setting element values in a 2D array.

Creating 2D Arrays in JavaScript

Regular arrays in JavaScript are one-dimensional. We create regular arrays by either using the literal notation or using the Array constructor as shown below:

1
let numbers = [1, 2, 4, 59, 589, 126];
2
let names = new Array('Peter', 'Adam', 'Andy', 'Jake', 'Andrew');
3
4
console.log(numbers);
5
// Outputs: [ 1, 2, 4, 59, 589, 126 ]

6
7
console.log(names);
8
// Outputs: [ 'Peter', 'Adam', 'Andy', 'Jake', 'Andrew' ]

We can create 2D arrays in a similar manner. Here is an example:

1
let matrix_a = [[1, 2, 4], [59, 589, 126], [34, 39, 27]];
2
let seating_arrangement = new Array(['Peter', 'Adam', 'Andy'], ['Jake', 'Andrew', 'Sam'], ['Jill', 'Jordan', 'Emma']);
3
4
console.log(matrix_a);
5
// Outputs: [ [ 1, 2, 4 ], [ 59, 589, 126 ], [ 34, 39, 27 ] ]

6
7
console.log(seating_arrangement);
8
/* Outputs:

9
[

10
  [ 'Peter', 'Adam', 'Andy' ],

11
  [ 'Jake', 'Andrew', 'Sam' ],

12
  [ 'Jill', 'Jordan', 'Emma' ]

13
]

14
*/

It is important to remember that unlike languages such as C++, 2D arrays in JavaScript do not have a fixed number of rows and columns. So if we want our array to be a rectangle—ie. with all the rows and columns the same length—we have to make sure that the subarrays all have the same length.

In our example above, we created our matrix_a array with three arrays. This gives three rows to our 2D array. Now each of those arrays had three elements of their own. This resulted in three columns in our 2D array. We could have limited our third array to only two elements and matrix_a would still be a 2D array. However, it would be a jagged 2D array.

For the rest of this tutorial, our focus will be on working with 2D arrays that have a fixed number of rows and columns. Let's define an array initialization function with these constraints in mind.

Initializing 2D Arrays in JavaScript

We know that a 2D array consists of an array of arrays which make up its rows and columns. Creating and initializing a 2D array is simply a matter of using the array constructor repeatedly to fill the elements of our main array. The following function does this for us:

1
function create_2d_array(rows, columns, value = 0) {
2
  let array_2d = new Array(rows);
3
  
4
  for(let i = 0; i < rows; i++) {
5
    array_2d[i] = new Array(columns).fill(value);
6
  }
7
  
8
  return array_2d;
9
}
10
11
matrix_a = create_2d_array(3, 4, 1);
12
console.log(matrix_a);
13
// Outputs: [ [ 1, 1, 1, 1 ], [ 1, 1, 1, 1 ], [ 1, 1, 1, 1 ] ]

14
15
matrix_b = create_2d_array(4, 3, "Pie");
16
console.log(matrix_b);
17
/* Outputs:

18
[

19
  [ 'Pie', 'Pie', 'Pie' ],

20
  [ 'Pie', 'Pie', 'Pie' ],

21
  [ 'Pie', 'Pie', 'Pie' ],

22
  [ 'Pie', 'Pie', 'Pie' ]

23
]

24
*/

Our function accepts three arguments, the first one is the number of rows which indicates how many arrays the parent array should contain. The second argument is the number of columns which dictates how many elements there should be within each array. Finally, we have the third argument which is the value that we want to set for each element in our 2D array.

Inside the function, we create a for loop that iterates over all the rows and fills the parent array element with new arrays. We call the fill() method on each of these new arrays in order to initialize them with our specified value. All the elements are set to 0 if no value is provided by the user.

We can make our array initialization function shorter using the built-in map() method. Here is the code for our newer and shorter function:

1
function create_2d_array_with_map(rows, columns, value = 0) {
2
  let array_2d = new Array(rows).fill(0);
3
  return array_2d.map(() => new Array(columns).fill(value));
4
}

As you can see above, this will give us the same output as the original function. This time, we begin as usual with our parent array of row elements. However, we called the fill() method to fill it with zeroes. After that, we called the map() method on this array which returned a new array with all the elements of the parent array filled with arrays of columns number of elements.

Some of you might now be wondering if we can make the array initialization function even shorter by using the fill() method twice instead of mapping the values as shown below:

1
function create_2d_array_wrong(rows, columns, value) {
2
  return new Array(rows).fill(new Array(columns).fill(value));
3
}

This will generate an array that looks the same as the previous ones, but with a subtle problem. Because the new Array(columns) constructor is only called once, all of the column arrays will actually be references to the exact same array. 

That means that you won't be able to update the individual columns independantly.

Getting and Setting Element Values in 2D Arrays

You probably already know that we can access an array element at any index by using the index value and the indexes are zero-based.  Here is an example:

1
let names = new Array('Peter', 'Adam', 'Andy', 'Jake', 'Andrew');
2
console.log(names[3]);
3
// Outputs: Jake

We got the fourth element in our array by using the index value 3. Let's try something similar with our 2D arrays.

1
let matrix_a = [[1, 2, 4], [59, 589, 126], [34, 39, 27]];
2
let seating_arrangement = new Array(['Peter', 'Adam', 'Andy'], ['Jake', 'Andrew', 'Sam'], ['Jill', 'Jordan', 'Emma']);
3
4
console.log(matrix_a[1]);
5
// Outputs: [ 59, 589, 126 ]

6
7
console.log(matrix_a[1][1]);
8
// Outputs: 589

9
10
console.log(seating_arrangement[0]);
11
// Outputs: [ 'Peter', 'Adam', 'Andy' ]

12
13
console.log(seating_arrangement[0][2]);
14
// Outputs: Andy

15

As you can see, using a single index value gives us an element of the parent array and this element is actually an array itself. We have to provide another index value to get our element from returned array.

Basically, the first index value gives us the array within which we should look for the element. We will call the first index value as row_index. The second index value gives us the element that we are looking for inside our array returned array. We will call the second index value as col_index. We can use the following statement to access any element we want inside our 2D array.

1
my_2d_array[row_idx][col_idx]

This will become more clear from the following code snippet:

1
let seating_arrangement = new Array(['Peter', 'Adam', 'Andy'], ['Jake', 'Andrew', 'Sam'], ['Jill', 'Jordan', 'Emma']);
2
3
console.table(seating_arrangement);
4
/* Outputs:

5
┌─────────┬─────────┬──────────┬────────┐

6
│ (index) │    0    │    1     │   2    │

7
├─────────┼─────────┼──────────┼────────┤

8
│    0    │ 'Peter' │  'Adam'  │ 'Andy' │

9
│    1    │ 'Jake'  │ 'Andrew' │ 'Sam'  │

10
│    2    │ 'Jill'  │ 'Jordan' │ 'Emma' │

11
└─────────┴─────────┴──────────┴────────┘

12
*/
13
14
console.log(seating_arrangement[0][2]);
15
// Outputs: Andy

16
17
18
console.log(seating_arrangement[2][0]);
19
// Outputs: Jill

Using console.table() to log our 2D arrays makes them easier to understand.

In the first case, we got the element from the first row (index 0) and the third column (index 2). This gave us the value Andy. Similarly, we got Jill by looking at the third row and first column.

Now that you know how to access element values at a particular row or index, it is easy to set a value for that element using simple assignment. Here is an example:

1
let seating_arrangement = new Array(['Peter', 'Adam', 'Andy'], ['Jake', 'Andrew', 'Sam'], ['Jill', 'Jordan', 'Emma']);
2
3
console.table(seating_arrangement);
4
/* Outputs:

5
┌─────────┬─────────┬──────────┬────────┐

6
│ (index) │    0    │    1     │   2    │

7
├─────────┼─────────┼──────────┼────────┤

8
│    0    │ 'Peter' │  'Adam'  │ 'Andy' │

9
│    1    │ 'Jake'  │ 'Andrew' │ 'Sam'  │

10
│    2    │ 'Jill'  │ 'Jordan' │ 'Emma' │

11
└─────────┴─────────┴──────────┴────────┘

12
*/
13
14
seating_arrangement[0][2] = 'Olivia';
15
16
seating_arrangement[2][0] = 'Ava';
17
18
console.table(seating_arrangement);
19
/* Outputs:

20
┌─────────┬─────────┬──────────┬──────────┐

21
│ (index) │    0    │    1     │    2     │

22
├─────────┼─────────┼──────────┼──────────┤

23
│    0    │ 'Peter' │  'Adam'  │ 'Olivia' │

24
│    1    │ 'Jake'  │ 'Andrew' │  'Sam'   │

25
│    2    │  'Ava'  │ 'Jordan' │  'Emma'  │

26
└─────────┴─────────┴──────────┴──────────┘

27
*/

Do you remember when I mentioned earlier that creating 2D arrays with two successive calls to the fill() method will create issues due to all elements referring to the same object? You can verify this yourself with the following code snippet:

1
function create_2d_array_wrong(rows, columns, value) {
2
  return new Array(rows).fill(new Array(columns).fill(value));
3
}
4
5
matrix_a = create_2d_array_wrong(3, 4, 1);
6
console.log(matrix_a);
7
// Outputs: [ [ 1, 1, 1, 1 ], [ 1, 1, 1, 1 ], [ 1, 1, 1, 1 ] ]

8
9
matrix_a[0][1] = 4;
10
11
console.log(matrix_a);
12
// Outputs: [ [ 1, 4, 1, 1 ], [ 1, 4, 1, 1 ], [ 1, 4, 1, 1 ] ]

We only changed to value of element in the first row and second column. However, the value in the second column was updated for all the rows.

Setting Whole Row or Column Values

We will now learn how to replace the value of entire row or column in a 2D array.

Replacing row values is easy because the whole row is basically an individual array. The only constraint that we will put on this replacement is that the replacing array needs to have the same number of elements as our original array. Here is our function that does the replacement:

1
function replace_row(array_2d, row_array, row_idx) {
2
  let array_2d_columns = array_2d[0].length;
3
  if(row_array.length == array_2d_columns) {
4
    array_2d[row_idx] = row_array;
5
  }
6
}
7
8
let seating_arrangement = new Array(['Peter', 'Adam', 'Andy'], ['Jake', 'Andrew', 'Sam'], ['Jill', 'Jordan', 'Emma']);
9
10
console.table(seating_arrangement);
11
/* Outputs:

12
┌─────────┬─────────┬──────────┬────────┐

13
│ (index) │    0    │    1     │   2    │

14
├─────────┼─────────┼──────────┼────────┤

15
│    0    │ 'Peter' │  'Adam'  │ 'Andy' │

16
│    1    │ 'Jake'  │ 'Andrew' │ 'Sam'  │

17
│    2    │ 'Jill'  │ 'Jordan' │ 'Emma' │

18
└─────────┴─────────┴──────────┴────────┘

19
*/
20
21
replace_row(seating_arrangement, ['Olivia', 'Ava', 'Sophia'], 1);
22
23
console.table(seating_arrangement;
24
/* Outputs:

25
┌─────────┬──────────┬──────────┬──────────┐

26
│ (index) │    0     │    1     │    2     │

27
├─────────┼──────────┼──────────┼──────────┤

28
│    0    │ 'Peter'  │  'Adam'  │  'Andy'  │

29
│    1    │ 'Olivia' │  'Ava'   │ 'Sophia' │

30
│    2    │  'Jill'  │ 'Jordan' │  'Emma'  │

31
└─────────┴──────────┴──────────┴──────────┘

32
*/

Replacing column values will require us to loop through all the rows because every element in a column comes from a different array. We will also include a condition that checks for a match in number of rows and the length of the replacing column array. Here is the code for our function:

1
function replace_column(array_2d, column_array, col_idx) {
2
  if(column_array.length == array_2d.length) {
3
    for(let i = 0; i < column_array.length; i++) {
4
      array_2d[i][col_idx] = column_array[i];
5
    }
6
  }
7
}
8
9
let seating_arrangement = new Array(['Peter', 'Adam', 'Andy'], ['Jake', 'Andrew', 'Sam'], ['Jill', 'Jordan', 'Emma']);
10
console.table(seating_arrangement);
11
/* Outputs:

12
┌─────────┬─────────┬──────────┬────────┐

13
│ (index) │    0    │    1     │   2    │

14
├─────────┼─────────┼──────────┼────────┤

15
│    0    │ 'Peter' │  'Adam'  │ 'Andy' │

16
│    1    │ 'Jake'  │ 'Andrew' │ 'Sam'  │

17
│    2    │ 'Jill'  │ 'Jordan' │ 'Emma' │

18
└─────────┴─────────┴──────────┴────────┘

19
*/
20
21
replace_column(seating_arrangement, ['Olivia', 'Ava', 'Sophia'], 1);
22
console.table(seating_arrangement);
23
/* Outputs:

24
┌─────────┬─────────┬──────────┬────────┐

25
│ (index) │    0    │    1     │   2    │

26
├─────────┼─────────┼──────────┼────────┤

27
│    0    │ 'Peter' │ 'Olivia' │ 'Andy' │

28
│    1    │ 'Jake'  │  'Ava'   │ 'Sam'  │

29
│    2    │ 'Jill'  │ 'Sophia' │ 'Emma' │

30
└─────────┴─────────┴──────────┴────────┘

31
*/

Final Thoughts

In this tutorial, we have covered the basics of 2D arrays in JavaScript. Their is no built-in support for 2D arrays in JavaScript. However, we can still create them easily by initializing an array of arrays. This tutorial showed us different ways of initializing 2D arrays. We also learned how to get or set the value of individual elements in our 2D array or replace the whole row or column at once.

You can also use the built-in array methods with these 2D arrays to implement additional functionality like splicing or slicing the 2D array.


No comments:

Post a Comment