Skip to content

Instantly share code, notes, and snippets.

@AaronO
Last active August 2, 2017 17:53
Show Gist options
  • Save AaronO/eb430ad7170142b13bad7548f9bcbe12 to your computer and use it in GitHub Desktop.
Save AaronO/eb430ad7170142b13bad7548f9bcbe12 to your computer and use it in GitHub Desktop.
Turn a 2d matrix into a flat array, of values in "clockwise order"
////
// Example (square) matrices
////
// 1x1
const M1 = [
[ 4 ],
];
const M2 = [
[1, 2],
[3, 4],
];
const M3 = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
];
const M4 = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16],
];
// Utility functions
function empty(arr) { return arr.length === 0; }
function first(arr) { return arr[0]; }
function middle(arr) { return arr.slice(1, arr.length - 1); }
function last(arr) { return arr[arr.length - 1]; }
function reverse(arr) { return arr.slice().reverse(); } // Return a reversed copy of an array (don't modify in place)
// Returns a 2d matrix inside a given 2d matrix by "stripping" the outer numbers
function MatrixMiddleNumbers(matrix) {
// Only keep the middle rows
return middle(matrix)
// Only keep the middle elements of the middle rows
.map(row => middle(row));
}
// Get the numbers on the outer edges of a 2d matrix (in clockwise order)
function MatrixOuterNumbers(matrix) {
const firstRow = first(matrix);
const middleRows = middle(matrix);
const lastRow = last(matrix);
// Edge case: if only one row in matrix, then just return that (because it's already in clockwise order)
if (matrix.length === 1) {
return firstRow;
}
return []
// All of first row
.concat(firstRow)
// Last elements of middle rows
.concat(middleRows.map(last))
// Last row (in reverse order)
.concat(reverse(lastRow))
// First elements of middle rows in reverse order
.concat(reverse(middleRows).map(first));
}
function MatrixToSpiralRecursive(matrix) {
// Stop recursing when matrix is empty (no rows left)
if (empty(matrix)) {
return [];
}
// Get outer numbers
const spiral = MatrixOuterNumbers(matrix);
// Get "sub" matrix
const subMatrix = MatrixMiddleNumbers(matrix);
// Recurse a run this same function on the smaller matrix
return spiral.concat(MatrixToSpiralRecursive(subMatrix));
}
function MatrixToSpiralImperative(matrix) {
// Get matrix's dimensions
const X = matrix[0].length;
const Y = matrix.length;
// Results
let result = [];
// Our starting x, y coordinates
let x = 0;
let y = 0;
// Our starting directions
let dx = 1;
let dy = 0;
// Number of remaining steps in each direction
let steps = 0;
let xRemaining = X;
let yRemaining = Y-1;
// Loop !
for (let i = 0; i < X*Y; i++) {
// Add current element
result.push(matrix[y][x]);
// Increase the number of steps we've advanced in the current direction
steps++;
// Change direction when we hit the remaining number of steps in the direction we're going
// Changing direction after going X
if (dx !== 0 && steps >= xRemaining) {
dy = dx;
dx = 0;
// Reset number of steps (since we're going in a new direction)
steps = 0;
xRemaining--;
} else if (dy !== 0 && steps >= yRemaining) {
// Changing direction after going Y
dx = -dy;
dy = 0;
steps = 0;
yRemaining--;
}
// Move forward given current direction
x += dx;
y += dy;
}
return result;
}
// Utility check function
function arrEquals(a1,a2) { return JSON.stringify(a1)==JSON.stringify(a2); } // Dirty but good enough for what we need
// Run tests
console.log('M1:', MatrixToSpiralImperative(M1));
console.log('M2:', MatrixToSpiralImperative(M2));
console.log('M3:', MatrixToSpiralImperative(M3));
console.log('M4:', MatrixToSpiralImperative(M4));
console.log(arrEquals(MatrixToSpiralImperative(M1), MatrixToSpiralRecursive(M1)));
console.log(arrEquals(MatrixToSpiralImperative(M2), MatrixToSpiralRecursive(M2)));
console.log(arrEquals(MatrixToSpiralImperative(M3), MatrixToSpiralRecursive(M3)));
console.log(arrEquals(MatrixToSpiralImperative(M4), MatrixToSpiralRecursive(M4)));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment