How to rotate an array in Javascript
To rotate a two-dimensional array or matrix by 90 degrees is sometimes necessary (e.g to twist a specific pattern or game template). With two small code snippets, most requirements can be easily implemented.
Let us first deal with the simpler part of the task, the rotation of a two-dimensional array with constant equal length of the rows, i.e. a classical matrix. For example, let's take a string array that looks like this:
const arr = [
['A', 'B', 'C', 'D'],
['E', 'F', 'G', 'H'],
['I', 'J', 'K', 'L'],
['M', 'N', 'O', 'P'],
];
90 degree rotation of a matrix
With a 90-degree rotation, it is to be expected that the first element of the last row after the rotation is at the top left, the first element of the penultimate row in second place and so on. The expected output would be like this:
const arr = [
['M', 'I', 'E', 'A'],
['N', 'J', 'F', 'I'],
['O', 'K', 'F', 'E'],
['P', 'E', 'I', 'A'],
];
So it becomes immediately clear that the algorithm has to put the cart before the horse. In Javascript, we can use the reverse
function on arrays, which flips the order of the array. Since this function mutates the original array, we are making a copy of the original array with the spread operator.
In a second step, the array is mapped in the new order and first the first, then the second, then the third element etc. of the new column is returned.They represent the new rows of the rotated array. The number of columns of the old element is the number of rows of the rotated array (since all rows of a matrix have the same length, arr[0].length
takes the length of index 0 inside the for-loop).The code then looks like this:
let _arr = [...arr].reverse(); // Makes a copy of arr, because reverse function would mutate original array
let newarr = [];
for (let i = 0; i < arr[0].length; i++) {
let newRow = [];
newRow = _arr.map((x) => {
return x[i];
});
newarr.push(newRow);
}
console.log(newarr);
Github Repository of the the first code snippet
Rotate a two-dimensional array with flexible line length
But, what if the row length of the array is different? For example, with the following array:
const arr = [
['A', 'B', 'C'],
['D', 'E', 'F', 'G', 'H', 'I'],
['J', 'K'],
['L', 'M', 'N', 'O'],
];
It is thus not possible to simply take the column length of index 0 for the loop, but rather the largest column length within the array must first be identified.
We use the built-in function Math.max
and apply
it on the length of all rows inside the array:
let arrMaxLength = Math.max.apply( // Get the max. row length inside array
null,
arr.map((x) => x.length)
);
A second change we have to make is within the map
function using the Array.from
function to fill up the shorter rows so that the rest of the functionality of the code snippet is preserved.
let _x = Array.from({ ...x, length: arrMaxLength });
In the above case, the array would be filled with undefined values, but if you want empty strings, the return has to be checked for undefined and if necessary an empty string has to be returned.
return _x[i] ? _x[i] : '';
In its complete form, the code snippet looks like this:
let arrMaxLength = Math.max.apply( // Get the max. row length inside array
null,
arr.map((x) => x.length)
);
let _arr = [...arr].reverse(); // Makes a copy of arr, because reverse function would mutate original array
let newarr = [];
for (let i = 0; i < arrMaxLength; i++) {
let newRow = [];
newRow = _arr.map((x) => {
let _x = Array.from({ ...x, length: arrMaxLength });
return _x[i]; // instead of undefined for empty fields you could return empty string: return _x[i] ? _x[i] : '';
});
newarr.push(newRow);
}
console.log(newarr);
The new rotated array would look like this:
const arr = [
['L', 'J', 'D', 'A'],
['M', 'K', 'E', 'B'],
['N', 'undefined', 'F', 'C'],
['O', 'undefined', 'G', 'undefined'],
['undefined', 'undefined', 'H', 'undefined'],
['undefined', 'undefined', 'I', 'undefined'],
];
Github Repository of the the second code snippet