I'm trying to come up with a solution that takes in a matrix like this:
[[1,2,3,4],
[5,6,7,8],
[9,10,11,12],
[13,14,15,16]]
and returns an array traversing the array as a spiral, so in this example:
[1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10]
I'm having trouble getting this recursive solution to work, in which the result array takes the first array, the final elements of the rest of the arrays, the bottom array in reverse order, and then the first elements of the middle arrays, and then reforms the array without that outer "shell" so that it can be recursively called on what's left until there's an array of one element in the center or a 2x2 matrix (my base cases, although the latter might not be necessary...)
My solution, which doesn't work, is as follows. Any suggestions on how I can make this work?
var spiralTraversal = function(matriks){
var result = [];
var goAround = function(matrix) {
var len = matrix[0].length;
if (len === 1) {
result.concat(matrix[0]);
return result;
}
if (len === 2) {
result.concat(matrix[0]);
result.push(matrix[1][1], matrix[1][0]);
return result;
}
if (len > 2) {
// right
result.concat(matrix[0]);
// down
for (var j=1; j < matrix.length - 1; j++) {
result.push(matrix[j][matrix.length -1]);
}
// left
for (var l=matrix.length - 2; l > 0; l--) {
result.push(matrix[matrix.length - 1][l]);
}
// up
for (var k=matrix.length -2; k > 0; k--) {
result.push(matrix[k][0]);
}
}
// reset matrix for next loop
var temp = matrix.slice();
temp.shift();
temp.pop();
for (var i=0; i < temp.length - 1; i++) {
temp[i] = temp[i].slice(1,-1);
}
goAround(temp);
};
goAround(matriks);
};
🌀 Spiral Array (ES6)
ES6 allows us to keep it simple:
function spiral(matrix) {
const arr = [];
while (matrix.length) {
arr.push(
...matrix.shift(),
...matrix.map(a => a.pop()),
...(matrix.pop() || []).reverse(),
...matrix.map(a => a.shift()).reverse()
);
}
return arr;
}
Your code is very close but it is doing more than it needs to do. Here I simplify and bug fix:
var input = [[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]];
var spiralTraversal = function(matriks){
var result = [];
var goAround = function(matrix) {
if (matrix.length == 0) {
return;
}
// right
result = result.concat(matrix.shift());
// down
for (var j=1; j < matrix.length - 1; j++) {
result.push(matrix[j].pop());
}
// bottom
result = result.concat(matrix.pop().reverse());
// up
for (var k=matrix.length -2; k > 0; k--) {
result.push(matrix[k].shift());
}
return goAround(matrix);
};
goAround(matriks);
return result;
};
var result = spiralTraversal(input);
console.log('result', result);
Running it outputs:
result [1, 2, 3, 4, 12, 16, 15, 14, 13, 5, 6, 7, 8, 11, 10, 9]
JSFiddle: http://jsfiddle.net/eb34fu5z/
Important things:
concat on Array returns the result -- it does not mutate the caller so you need to save the result of the concat like so: result = result.concat(otherArray)
check the terminating condition at top of recursive array
for each pass, do the expected (top, right, bottom, left)
return the result
Here is how I would do it but I would add error checking to verify the array has an equal number of "rows" and "columns". So assuming the input is valid, here we go:
var input = [[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]];
function run(input, result) {
if (input.length == 0) {
return result;
}
// add the first row to result
result = result.concat(input.shift());
// add the last element of each remaining row
input.forEach(function(rightEnd) {
result.push(rightEnd.pop());
});
// add the last row in reverse order
result = result.concat(input.pop().reverse());
// add the first element in each remaining row (going upwards)
var tmp = [];
input.forEach(function(leftEnd) {
tmp.push(leftEnd.shift());
});
result = result.concat(tmp.reverse());
return run(input, result);
}
var result = run(input, []);
console.log('result', result);
Which outputs:
result [1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10]
The general idea is we know for each pass we need to do these things:
Add the first array in input
Add the last item from each remaining array in input
Add the last array in input
Add the first item from each remaining array in input
So if we do the recursion with doing that at each pass, we can accomplish the spiraling.
JSFiddle: http://jsfiddle.net/2v6k5uhd/
This solution is for any kind of matrix (m * n), not just square(m * m). Below example takes 5*4 matrix and prints in spiral format.
var matrix = [[1,2,3,4], [14,15,16,5], [13,20,17,6], [12,19,18,7], [11,10,9,8]];
var row = currentRow = matrix.length, column = currentColumn = matrix[0].length;
while(currentRow > row/2 ){
// traverse row forward
for(var i = (column - currentColumn); i < currentColumn ; i++) { console.log(matrix[row - currentRow][i]); }
// traverse column downward
for(var i = (row - currentRow + 1); i < currentRow ; i++) { console.log(matrix[i][currentColumn - 1]) }
// traverse row backward
for(var i = currentColumn - 1; i > (column - currentColumn) ; i--) { console.log(matrix[currentRow - 1][i - 1]); }
// traverse column upward
for(var i = currentRow - 1; i > (row - currentRow + 1) ; i--) { console.log(matrix[i - 1][column - currentColumn]) }
currentRow--;
currentColumn--;
}
Your algorithm seems fine, there is only one mistake There are a few things, some more hard to spot than others.
The concat method does not alter the array (like push does), but returns a new array that contains all the elements from the original array and the arguments. The result is not mutated.
To fix this, you could either
use result = result.concat(…);
make it an explicit loop where you do result.push(…) (like the down, left and up ones you already wrote) or
use result.push.apply(result, …) to push multiple values at once
Your "left" or "up" loop does miss one element, the bottom left one. Either when going left, you need advance to the first element (use >= 0 in the condition), or when going up you will need to start in the last instead of the second-to-last row (matrix.length-1)
In the loop that shrinks the matrix for the next iteration you forgot the last row, it needs to be for (var i=0; i < temp.length; i++) (not temp.length-1). Otherwise you get very unfortunate results.
Your base case should be 0 (and 1), not (1 and) 2. This will both simplify your script and avoid errors (in edge cases).
You expect your matrices to be square, but they could be rectangular (or even have lines of uneven length). The .length you are accessing might be not the one you expect - better doublecheck and throw an error with a descriptive message.
Both spiralTraversal and goAround are missing a return statement for the (recursive) call. They just fill up result but don't return anything.
Recursive Solution:
Instead of going around, I just go over the top row, and the rightmost column, then recursively call the function on the "reversed" matrix.
var input = [
[ 1, 2, 3, 4],
[ 5, 6, 7, 8],
[ 9,10,11,12],
[13,14,15,16]
];
let spiral = (mat) => {
if(mat.length && mat[0].length) {
mat[0].forEach(entry => { console.log(entry)})
mat.shift();
mat.forEach(item => {
console.log(item.pop())
});
spiral(reverseMatrix(mat))
}
return;
}
let reverseMatrix = (mat) => {
mat.forEach(item => {
item.reverse()
});
mat.reverse();
return mat;
}
console.log("Clockwise Order is:")
spiral(input)
Here my function :
let array_masalah = [
[1,2,3,4],
[5,6,7,8],
[9, 10, 11, 12],
[13, 14, 15,16],
];
let array_masalah_2 = [
[1, 2, 3, 4, 5],
[6, 7, 8, 9, 10],
[11, 12, 13, 14, 15],
[16, 17, 18, 19, 20],
];
function polaSpiral(array_masalah) {
function spiral(array) {
if (array.length == 1) {
return array[0];
}
var firstRow = array[0]
, numRows = array.length
, nextMatrix = []
, newRow
, rowIdx
, colIdx = array[1].length - 1
for (colIdx; colIdx >= 0; colIdx--) {
newRow = [];
for (rowIdx = 1; rowIdx < numRows; rowIdx++) {
newRow.push(array[rowIdx][colIdx]);
}
nextMatrix.push(newRow);
}
firstRow.push.apply(firstRow, spiral(nextMatrix));
return firstRow
}
console.log(spiral(array_masalah));
}
polaSpiral(array_masalah) // [ 1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10 ]
polaSpiral(array_masalah_2) // [ 1, 2, 3, 4, 5, 10, 15, 20, 19, 18, 17, 16, 11, 6, 7, 8, 9, 14, 13, 12 ]
While not recursive, it at least outputs the correct answer of:
result: [ 1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10 ]
I'd say the only weird thing about this is having to "reset" the variables i,j after each while loop. Also, there's probably a cleaner recursive solution.
var array = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
];
function spiralTraversal(array) {
let discovered = new Set();
let result = [];
let totalSpots = array.length * array[0].length;
let direction = 'right';
for (var i = 0; i < array.length; i ++) {
for (var j = 0; j < array[i].length; j++) {
while (totalSpots) {
while (direction === 'right' && !!bounds(array, i, j) && !discovered.has(array[i][j])) {
discovered.add(array[i][j]);
result.push(array[i][j]);
totalSpots--;
j++;
}
direction = 'down';
i++;
j--;
while (direction === 'down' && !!bounds(array,i, j) && !discovered.has(array[i][i])) {
discovered.add(array[i][j]);
result.push(array[i][j]);
totalSpots--;
i++;
}
direction = 'left';
j--;
i--;
while (direction === 'left' && !!bounds(array, i, j) && !discovered.has(array[i][j])) {
discovered.add(array[i][j]);
result.push(array[i][j]);
totalSpots--;
j--;
}
direction = 'up';
i--;
j++
while (direction === 'up' && bounds(array, i, j) && !discovered.has(array[i][j])) {
discovered.add(array[i][j]);
result.push(array[i][j]);
totalSpots--;
i--;
}
direction = 'right';
j++;
i++;
}
}
}
return result;
}
function bounds(array, i, j){
if (i < array.length && i >= 0 && j < array[0].length && j >= 0) {
return true;
} else {
return false;
}
};
const spiralOrder = matrix => {
if (!matrix || matrix.length === 0) {
return [];
}
let startRow = 0;
let startCol = 0;
let ans = [];
let endCol = matrix[0].length - 1;
let endRow = matrix.length - 1;
while (startRow <= endRow && startCol <= endCol) {
for (let i = startCol; i <= endCol; i++) {
ans.push(matrix[startRow][i]);
}
startRow++;
for (let i = startRow; i <= endRow; i++) {
ans.push(matrix[i][endCol]);
}
endCol--;
if (startRow <= endRow) {
for (let i = endCol; i >= startCol; i--) {
ans.push(matrix[endRow][i]);
}
endRow--;
}
if (startCol <= endCol) {
for (let i = endRow; i >= startRow; i--) {
ans.push(matrix[i][startCol]);
}
startCol++;
}
}
return ans;
};
let input = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
//Output: [1, 2, 3, 6, 9, 8, 7, 4, 5];
spiralOrder(input);
Below is a Javascript solution. I have added comments to the code so you can follow along with the process :)
var array = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16]
];
var n = array.length;
//create empty 2d array
var startRow = 0;
var endRow = n - 1;
var startColumn = 0;
var endColumn = n - 1
var newArray = [];
// While loop is used to spiral into the 2d array.
while(startRow <= endRow && startColumn <= endColumn) {
// Reading top row, from left to right
for(var i = startColumn; i <= endColumn; i++) {
newArray.push(array[startColumn][i]);
}
startRow++; // Top row read.
// Reading right column from top right to bottom right
for(var i = startRow; i <= endRow; i++) {
newArray.push(array[i][endColumn]);
}
endColumn--; // Right column read
// Reading bottom row, from bottom right to bottom left
for(var i = endColumn; i >= startColumn; i--) {
newArray.push(array[endRow][i]);
}
endRow--; // Bottom row read
// Reading left column, from bottom left to top left
for(var i = endRow; i >= startRow; i--) {
newArray.push(array[i][startColumn]);
}
startColumn++; // left column now read.
} // While loop will now spiral in the matrix.
console.log(newArray);
:)
ES6 JS version with clockwise and anticlockwise spiral traversal.
function traverseSpiral(arr2d,directionMap,initialPos){
// Initializing length.
const len = arr2d.length;
let totalElementsTraversed =0;
// Elements in the first line is equal to the array length.
// (as this is a square matrix)
let elementsInLine=len;
let elementsTraversedInRow = 0;
let linesCompleted = 1;
let direction = initialPos[0] === 0 ? 'r' : 'd';
// Function to move in the desired direction.
const move = checkDirectionAndMove(initialPos);
const spiralArray = [];
while( totalElementsTraversed!==len*len){
// On Each line completion
if(elementsTraversedInRow===elementsInLine){
linesCompleted++;
// Reset elements traversed in the row.
elementsTraversedInRow =0;
// After each line completion change direction.
direction = directionMap.get(direction);
// For every 2 traversed lines elements in the line will decrease.
if(linesCompleted % 2===0) elementsInLine--;
}
// Update elements traversed
totalElementsTraversed+=1
elementsTraversedInRow+=1;
// Move in the defined direction
const [ down,right] = move(direction);
spiralArray.push(arr2d[down][right]);
}
return spiralArray;
}
function checkDirectionAndMove(initialPosition) {
// Unpack array to variables
let [down,right] = initialPosition;
// Return function.
return (direction)=> {
// Switch based on right/left/up/down direction.
switch(direction){
case 'r':
right++;
break;
case 'l':
right--;
break;
case 'd':
down++;
break;
default :
down--;
}
return [down,right]
}
}
// If current direction is right move down and so on....
const clockWiseMap = new Map(Object.entries({
'r':'d',
'd':'l',
'l':'u',
'u':'r'
}));
// If current direction is right move up and so on....
const antiClockWiseMap = new Map(Object.entries({
'r':'u',
'd':'r',
'l':'d',
'u':'l'
}));
// Spiral traversal in the clockwise direction.
const clockWiseSpiralTraversal = traverseSpiral(
[[1, 2, 3, 4, 5],
[16, 17, 18, 19, 6],
[15, 24, 25, 20, 7],
[14, 23, 22, 21, 8],
[13, 12, 11, 10, 9]],
clockWiseMap,
[0,-1]
)
// Spiral traversal in the anti-clockwise direction.
const antiClockWiseSpiralTraversal = traverseSpiral(
[[1, 2, 3, 4, 5],
[16, 17, 18, 19, 6],
[15, 24, 25, 20, 7],
[14, 23, 22, 21, 8],
[13, 12, 11, 10, 9]],
antiClockWiseMap,
[-1,0]
)
console.log("Clock wise traversal :", clockWiseSpiralTraversal)
console.log("Anti-clock wise traversal :",antiClockWiseSpiralTraversal)
Returns :
Clock wise traversal : [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 ]
Anti-clock wise traversal : [ 1, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 17, 24, 23, 22, 21, 20, 19, 18, 25 ]
Main used techniques and others:
Closure, Arrow functions,
Higher-order functions, Conditional/ternary operator, Array destructuring,
and JS map.
I'm use to C#:
public static IList<int> spiralTraversal (int[,] matrix)
{
IList<int> list = new List<int>();
// Get all bounds before looping.
int bound0 = matrix.GetUpperBound(0);
int bound1 = matrix.GetUpperBound(1);
int totalElem = (bound0+1) * (bound1+1);
int auxbound0 = 0;
int auxbound1 = 0;
string direction = "left";
int leftCtrl = 0;
int rightCtrl = 0;
int upCtrl = 0;
int downCtrl = 0;
for (int i=0;i< totalElem;i++)
{
if (direction == "down")
{
list.Add(matrix[auxbound0, auxbound1]);
if (auxbound0 == bound0 - downCtrl)
{
direction = "right";
auxbound1 -= 1;
downCtrl += 1;
continue;
}
else
{
auxbound0 += 1;
}
}
if (direction == "left")
{
list.Add(matrix[auxbound0, auxbound1]);
if (auxbound1 == bound1 - leftCtrl)
{
direction = "down";
auxbound0 += 1;
leftCtrl += 1;
continue;
}
else
{
auxbound1 += 1;
}
}
if (direction == "up")
{
list.Add(matrix[auxbound0, auxbound1]);
if (auxbound0 == 1 + upCtrl)
{
direction = "left";
auxbound1 += 1;
upCtrl += 1;
continue;
}
else
{
auxbound0 -= 1;
}
}
if (direction == "right")
{
list.Add(matrix[auxbound0, auxbound1]);
if (auxbound1 == rightCtrl)
{
direction = "up";
auxbound0 -= 1;
rightCtrl += 1;
continue;
}
else
{
auxbound1 -= 1;
}
}
}
return list;
}
This solution takes spiral array and converts it to Ordered Array.
It Sorts Spiral Matrix with the format of Top, Right, Bottom, Left.
const matrix = [
[1, 2, 3, 4, 5],
[16, 17, 18, 19, 6],
[15, 24, 25, 20, 7],
[14, 23, 22, 21, 8],
[13, 12, 11, 10, 9],
];
function getOrderdMatrix(matrix, OrderdCorner) {
// If the Matrix is 0 return the OrderdCorner
if (matrix.length > 0) {
//Pushes the top of the matrix to OrderdCorner array
OrderdCorner.push(...matrix.shift());
let left = [];
/*Pushes right elements to the Orderdcorner array and
Add the left elements to the left array */
for (let i = 0; i < matrix.length; i++) {
OrderdCorner.push(matrix[i][matrix[i].length - 1])
matrix[i].pop(); //Remove Right element
if (matrix[i].length > 0) {
//Starts from the last element of the left corner
left.push(matrix[(matrix.length - 1) - i][0])
matrix[(matrix.length - 1) - i].shift();
}
}
/* If the array length is grater than 0 add the bottom
to the OrderdCorner array */
if (matrix.length > 0) {
OrderdCorner.push(...matrix.pop().reverse());
}
//Ads the left array to the OrderdCorner array
OrderdCorner.push(...left);
return getOrderdMatrix(matrix, OrderdCorner);
} else {
return OrderdCorner
}
}
console.log(getOrderdMatrix(matrix,[]));
Returns
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25]
Here's a configurable version :
function spiral(n) {
// Create 2D array of size n*n
var matrix = new Array(n);
for(var i=0; i < matrix.length; i++) {
matrix[i] = new Array(n);
}
for(var i=0; i < n;i++) {
for(var j=0; j < n; j++) {
matrix[i][j] = 0;
}
}
var startNum = 0;
var rowNum = 0;
function spin(rowNum) {
// right
for(var j=rowNum; j < (n-rowNum); j++) {
startNum++;
matrix[rowNum][j] = startNum;
}
if(startNum === (n*n)) {
return; // exit if number matches to the size of the matrix. ( 16 = 4*4 )
}
// down
for(var i=(rowNum+1); i < (n-(rowNum+1)); i++) {
startNum++;
matrix[i][n-(rowNum+1)] = startNum;
}
if(startNum === (n*n)) {
return; // exit if number matches to the size of the matrix. ( 16 = 4*4 )
}
// left
for(var j=(n-(1+rowNum)); j >= rowNum; j--) {
startNum++;
matrix[(n-(1+rowNum))][j] = startNum;
}
if(startNum === (n*n)) {
return; // exit if number matches to the size of the matrix. ( 16 = 4*4 )
}
//top
for(var i=(n-(2+rowNum)); i > rowNum; i--) {
startNum++;
matrix[i][rowNum] = startNum;
}
if(startNum === (n*n)) {
return; // exit if number matches to the size of the matrix. ( 16 = 4*4 )
}
spin(rowNum+1);
}
spin(rowNum);
console.log(matrix)
}
spiral(6);
Example : https://jsfiddle.net/dino_myte/276ou5kb/1/
I have written an article for a while about this beautiful toy problem, I really enjoy it. you might need to check out my solution.
you can follow me on Medium and you can check my article from here.
var spiralTraversal = function (matrix, result = []) {
// TODO: Implement me!
// if the length of the matrix ==0 we will return the result
if (matrix.length == 0) {
return result;
}
// we need to push the elements inside the first element of the array then delete this element
while (matrix[0].length) {
result.push(matrix[0].shift());
}
//top right to bottom right
matrix.forEach((row) => {
result.push(row.pop());
});
//bottom right to bottom left
while (matrix[matrix.length - 1].length) {
result.push(matrix[matrix.length - 1].pop());
}
//reverse again so we can retraverse on the next iteration
matrix.reverse();
//filter out any empty arrays
matrix = matrix.filter((element) => element.length);
//recursive case
result = spiralTraversal(matrix, result);
//return the result and filter any undefined elements
return result.filter((element) => element);
};
Related
I have a matrix and a flip_value that goes for the first element, I want to offset or push down the numbers of a selected column.
For example
let matrix = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16],
];
I want to change the matrix-like that:
So in the second (1st row in the program) is pushed down by 1 and the first element changed.
flip_value = 999
let matrix = [1, 999, 3, 4],
[5, 2, 7, 8],
[9, 6, 11, 12],
[13, 10, 15, 16],
The new flip value is 14.
Could you also give me a hint on how to do this from down to up?
flip_value = 999
Like that:
let matrix = [
[1, 6, 3, 4],
[5, 10, 7, 8],
[9, 14, 11, 12],
[13, 999, 15, 16],
];
//In this method the second-row values are pushed down to up by 1 and the last value in second row the last element flipped.
The new flip_value = 2 here
The whole code which is not working
let matrix = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16],
];
let Yindex = 1;
let flip_value = 999;
let size = 4;
let flip_tmp = matrix[size - 1][Yindex];
console.log("fontos");
for (let i = 1; i < size; i++) {
// matrix[i][Yindex] = matrix[i-1][Yindex];
let tmp = matrix[i][Yindex];
matrix[i][Yindex] = matrix[i - 1][Yindex];
matrix[i - 1][Yindex] = tmp;
console.log(matrix[i][Yindex] + "=" + matrix[i - 1][Yindex]);
}
matrix[0][Yindex] = flip_value;
flip_value = flip_tmp;
for (let i = 0; i < size; i++) {
console.log("\n");
for (let j = 0; j < size; j++) {
console.log(matrix[i][j] + " ");
}
}
for (let i = 0; i < size; i++) {
console.log("\n");
for (let j = 0; j < size; j++) {
if (j == 1) {
console.log(i + "" + j + " " + matrix[i][j] + " ");
}
}
}
From down to up or from up to down are pretty similar, speaking of the logic.
There are just three things that changes between them:
Change\Type of push
Up-down
Down-up
flip_tmp (1)
matrix[0][Yindex]
matrix[size - 1][Yindex]
for-loop (2)
for(i = 1; i < size; i++)
for(i = size - 1; i > 0; i--)
matrix's flip value (3)
matrix[size - 1][Yindex]
matrix[0][Yindex]
With this said, you can adapt your code to handle either case by passing it as a string, for example.
let pushDirection = "up-down"; // or down-up if not specified
let Yindex = 1;
let flip_value = 999;
let flip_tmp;
function swap(matrix, i, j){
let tmp = matrix[i][j];
matrix[i][j] = matrix[i - 1][j];
matrix[i - 1][j] = tmp;
}
let matrix = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16],
];
let size = matrix.length
if(pushDirection === "up-down"){
flip_tmp = matrix[0][Yindex]; // 1
for(let i = 1; i < size; i++) { // 2
swap(matrix, i, Yindex);
}
matrix[size - 1][Yindex] = flip_value; // 3
}
else { // down-up
flip_tmp = matrix[size - 1][Yindex]; // 1
for(let i = size-1; i > 0; i--) { // 2
swap(matrix, i, Yindex);
}
matrix[0][Yindex] = flip_value; // 3
}
flip_value = flip_tmp;
console.log("Push direction:", pushDirection, "\n", JSON.stringify(matrix).replaceAll('],','],\n'))
console.log("New flip value: ", flip_value)
Abstraction is a useful idea here. First, the matrix can abstracted as object with accessors that hide the internal representation. The accessors the OP needs are get/set column.
An array that rotates and keeps state is another tool that solves this problem (and maybe others).
class Matrix {
constructor(arrayOfArrays) {
this.array = arrayOfArrays
}
getColumn(j) {
// out of bounds check for the reader
return this.array.map(row => row[j])
}
setColumn = (j, colValues) => {
// array length and out of bounds check for the reader
this.array.forEach((row, i) => row[j] = colValues[i])
}
// just for illustration.. more like this left to the reader
getRow(i) { return this.array[i] }
print() {
this.array.forEach(row => console.log(JSON.stringify(row)))
}
}
// array that rotates and keeps state about the last value popped
class RotatingArray {
constructor(array) {
this.array = array
this.rotateValue = null
}
rotateForward(value) {
value = value || this.rotateValue
this.rotateValue = this.array[this.array.length-1]
this.array = [value, ...this.array.slice(0, -1)];
return this.array
}
rotateReverse(value) {
value = value || this.rotateValue
this.rotateValue = this.array[0]
this.array = [...this.array.slice(1), value];
return this.array
}
}
// those are the tools needed to solve the problem
// testing, rotate the 1st col forward with 999, do it twice
let data = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12],
[13, 14, 15, 16],
];
let matrix = new Matrix(data)
let firstCol = matrix.getColumn(1);
let rArr = new RotatingArray(firstCol);
firstCol = rArr.rotateForward(999)
matrix.setColumn(1, firstCol);
matrix.print();
firstCol = rArr.rotateForward()
matrix.setColumn(1, firstCol);
console.log('after a second rotation')
matrix.print();
Very new to coding so please bear with me. I am attempting to solve this Kata on Codewars: https://www.codewars.com/kata/snail/train/javascript
Basically given an array like
[
[1, 2, 3, 4],
[12,13,14,5],
[11,16,15,6],
[10,9, 8, 7]
];
It would return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16].
A snail trail spiraling around the outside of the matrix and inwards.
I am just solving for the case where the matrix is n x n where n is > 1 and an even number for now.
I got it working by declaring outputarray outside the function but I want that array to be declared within the function, hence the inclusion of this line: var outputarray = outputarray || [];
Not sure where I am going wrong.
snail = function(array) {
if (array.length == 0) {
return outputarray
}
var n = array[0].length - 1;
var outputarray = outputarray || [];
for (var i = 0; i <= n; i++) {
outputarray.push(array[0].splice(0, 1));
}
for (var i = 1; i <= n; i++) {
outputarray.push(array[i].splice(n, 1));
}
for (var i = n - 1; i >= 0; i--) {
outputarray.push(array[n].splice(i, 1));
}
for (var i = n - 1; i > 0; i--) {
outputarray.push(array[i].splice(0, 1));
}
array.pop();
array.shift();
snail(array);
}
Here's a non-recursive approach that doesn't mutate the input array. It works by keeping track of the top-left coordinate x, y and the size n of the spiral.
snail = function(array) {
const { length } = array;
const result = [];
let x = 0;
let y = 0;
let n = length;
while (n > 0) {
// travel right from top-left of spiral
for (let i = x; i < x + n; ++i) result.push(array[y][i]);
// shrink spiral and move top of spiral down
n--; y++;
// travel down from top-right of spiral
for (let i = y; i < y + n; ++i) result.push(array[i][x + n]);
// travel left from bottom-right of spiral
for (let i = x + n - 1; i >= x; --i) result.push(array[y + n - 1][i]);
// shrink spiral
n--;
// travel up from bottom-left of spiral
for (let i = y + n - 1; i >= y; --i) result.push(array[i][x]);
// move left of spiral right
x++;
}
return result;
}
console.log(snail([[1, 2, 3, 4], [12, 13, 14, 5], [11, 16, 15, 6], [10, 9, 8, 7]]));
One option is to define another function inside snail, which calls itself recursively, while also defining the outputarray inside snail. That way, outputarray isn't exposed to the outer scope, but the recursive function can still see it.
Also note that splice returns an array, so right now, your outputarray gets composed of an array of arrays. Spread into push instead to fix it, so that the outputarray becomes an array of numbers:
const input = [
[1, 2, 3, 4],
[12, 13, 14, 5],
[11, 16, 15, 6],
[10, 9, 8, 7]
];
const snail = (array) => {
const outputarray = [];
const iter = () => {
if (array.length == 0) {
return
}
var n = array[0].length - 1;
for (var i = 0; i <= n; i++) {
outputarray.push(...array[0].splice(0, 1));
}
for (var i = 1; i <= n; i++) {
outputarray.push(...array[i].splice(n, 1));
}
for (var i = n - 1; i >= 0; i--) {
outputarray.push(...array[n].splice(i, 1));
}
for (var i = n - 1; i > 0; i--) {
outputarray.push(...array[i].splice(0, 1));
}
array.pop();
array.shift();
iter(array);
};
iter(array);
return outputarray;
}
console.log(snail(input));
You could take some borders for the left, right, upper and lower indices and loop until no more indices are available.
function snail(array) {
var upper = 0,
lower = array.length - 1,
left = 0,
right = array[0].length - 1,
i = upper,
j = left,
result = [];
while (true) {
if (upper++ > lower) break;
for (; j < right; j++) result.push(array[i][j]);
if (right-- < left) break;
for (; i < lower; i++) result.push(array[i][j]);
if (lower-- < upper) break;
for (; j > left; j--) result.push(array[i][j]);
if (left++ > right) break;
for (; i > upper; i--) result.push(array[i][j]);
}
result.push(array[i][j]);
return result;
}
console.log(...snail([[1, 2, 3, 4], [12, 13, 14, 5], [11, 16, 15, 6], [10, 9, 8, 7]]));
This may not be according to the rules (or spirit?) of the kata, however, you can just glue it all together and sort.
function snail(trail) {
const numeric = (a, b) => a - b
const gather = (items, item) => items.push(parseInt(item, 10)) && items
const inline = (route, points) => points.reduce(gather, route) && route
const order = paths => paths.reduce(inline, []).sort(numeric)
return order(trail)
}
const trail = [
[1, 2, 3, 4],
[12, 13, 14, 5],
[11, 16, 15, 6],
[10, 9, 8, 7]
]
console.log(JSON.stringify(snail(trail)))
Try this:
const input = [
[1, 2, 3, 4],
[12, 13, 14, 5],
[11, 16, 15, 6],
[10, 9, 8, 7]
];
function snail(array) {
var res = [];
if (!array.length) return res;
var next = array.shift();
if (next) res = res.concat(next);
for (var i = 0; i < array.length; i++) {
res.push(array[i].pop());
}
next = array.pop()
if (next) res = res.concat(next.reverse());
for (var i = array.length - 1; i >= 0; i--) {
res.push(array[i].shift());
}
return res.concat(snail(array));
}
console.log(snail(input));
Here's my two cents, using the customary recursion:
function f(A){
return A.length > 1 ? A.splice(0,1)[0].concat(
f(A[0].map((c, i) => A.map(r => r[i])).reverse())) : A[0]
}
var A = [[ 1, 2, 3, 4], [12,13,14, 5], [11,16,15, 6], [10, 9, 8, 7]]
console.log(JSON.stringify(f(A)))
The task:
You are given 3 sorted arrays. You should find the smallest number
that is common in all 3 arrays and return it. If such a number doesn't
exist, return -1
My approach:
Use early exit: If one of the array is empty, then we know there won't be a common number in all arrays
Create pointer that points to the first element of the corresponding array
In the outer loop, loop through all elements of the first array. If any of the pointer over reaches the end of the array, then break the loop because we have reached the end and haven't found a common number
In the first inner loop check the second array for the element the pointer of the first array is pointing to
If you found it then set a flag. Otherwise remember the running index to the second pointer so we can continue at this element in the next iteration
If the flag is set, then loop through the second inner loop. In here it's analogous to the first inner loop: Search for the current element in the 3rd array.
If you find it, then immediately return the value. If not go to the next element until you find an element bigger than the current one
Increment the pointer of the first array
Do this until you reach either one of the array
My Solution:
let findLeastCommonNumber = function(a, b, c) {
if (a.length === 0 || b.length === 0 || c.length === 0) {
return -1;
}
let aPointer = 0;
let bPointer = 0;
let cPointer = 0;
while (aPointer < a.length ||
bPointer < b.length ||
cPointer < c.length) {
const aValue = a[aPointer];
let bFound = false;
for (let i = bPointer; i < b.length; i++) {
if (b[i] === aValue) {
bPointer = i;
bFound = true;
break;
}
if (b[i] > aValue) {
bPointer = i;
break;
}
}
if (bFound) {
for (let i = cPointer; i < c.length; i++) {
if (c[i] === aValue) {
return a[aPointer];
}
if (c[i] > aValue) {
cPointer = i;
break;
}
}
}
aPointer++;
}
return -1;
};
Sample solution:
let find_least_common_number = function(a, b, c) {
let i = 0;
let j = 0;
let k = 0;
while (i < a.length
&& j < b.length
&& k < c.length) {
// Finding the smallest common number
if (a[i] === b[j]
&& b[j] === c[k]) {
return a[i];
}
// Let's increment the iterator
// for the smallest value.
if (a[i] <= b[j]
&& a[i] <= c[k]) {
i++;
} else if (b[j] <= a[i]
&& b[j] <= c[k]) {
j++;
} else if (c[k] <= a[i]
&& c[k] <= b[j]) {
k++;
}
}
return -1;
};
I like the fact that the sample solution has less nesting. But the sample solution doesn't take advantage of an early exit and I think my solution is more scalable. Let's say the requirements changes, and 27 arrays more are now included. In my solution, I'd just copy the inner loops and change the pointer names only. I don't need to touch existing code. In the sample solution, however, I'd have touch every line of code that refers to one of the arrays and I would add the new arrays there. What do you think?
You could a completely dynamic approach for an infinite (sort of) count of arrays.
function findLeastCommonNumber(...array) {
var indices = array.map(_ => 0),
smallest = Math.max(...array.map((a, i) => a[indices[i]])),
next;
while (indices.every((i, j) => i < array[j].length)) {
next = smallest;
array.forEach((a, i) => {
while (indices[i] < a.length && a[indices[i]] < smallest)
next = Math.max(next, a[++indices[i]]);
});
if (array.every((a, i) => a[indices[i]] === smallest)) return smallest;
smallest = next;
}
return -1;
}
console.log(findLeastCommonNumber([1, 2, 3, 4, 5, 7], [8, 9, 10], [1, 2, 3, 5, 6, 7, 9]));
console.log(findLeastCommonNumber([1, 2, 3, 4, 5, 7, 9, 10], [1, 2, 3, 5, 6, 7, 9, 10], [4, 6, 7, 8, 9, 10, 11, 12]));
console.log(findLeastCommonNumber([1, 5, 6, 7, 8, 10], [5, 6, 9, 10], [1, 2, 3, 4, 5, 6, 9, 10]));
For the more readable solution, you can use this:
const findLeastCommonNumber = function() {
const total = [].concat(...arguments).sort((a,b) => a > b ? 1 : -1);
let index = 0;
let commonNumber = -1;
while(total.length - 2 > index && commonNumber === -1){
if(total[index] === total[index + 1] && total[index] === total[index + 2]){
commonNumber = total[index];
}
index++;
}
return commonNumber;
};
console.log(findLeastCommonNumber([1,5,6,7,8,10],[5,6,9,10],[1,2,3,4,5,6,9,10]));
An alternative solution - Take the 1st array, and convert the other arrays to Sets. Now use Array.find() on the 1st array, and check with Array.every() if the current number is found in all sets.
const findLeastCommonNumber = (main, ...array) => {
const sets = array.map(o => new Set(o));
const common = main.find(n => sets.every(s => s.has(n)));
return common === undefined ? -1 : common;
};
console.log(findLeastCommonNumber([1, 2, 3, 4, 5, 7], [8, 9, 10], [1, 2, 3, 5, 6, 7, 9]));
console.log(findLeastCommonNumber([1, 2, 3, 4, 5, 7, 9, 10], [1, 2, 3, 5, 6, 7, 9, 10], [4, 6, 7, 8, 9, 10, 11, 12]));
console.log(findLeastCommonNumber([1, 5, 6, 7, 8, 10], [5, 6, 9, 10], [1, 2, 3, 4, 5, 6, 9, 10]));
The arrays are sorted in ascending order. We will use three
iterators simultaneously to traverse each of the arrays. We can
start traversing each array from the 0^{th}index, which always has
the smallest value.
If the values pointed to by the three iterators are equal, that is
the solution. Since the arrays are sorted in ascending order, that
value must be the smallest value present in all of the arrays.
Otherwise, we see which of the three iterators points to the
smallest value and increment that iterator so that it points to the
next index.
If any of the three iterators reaches the end of the array before
we find the common number, we return -1.
let findLeastCommonNumber = function(arr1, arr2, arr3) {
// Initialize starting indexes for arr1, arr2 and arr3
let i = 0;
let j = 0;
let k = 0;
// Iterate through three arrays
while (i < arr1.length && j < arr2.length && k < arr3.length) {
// Finding the smallest common number
if (arr1[i] === arr2[j] && arr2[j] === arr3[k]) {
return arr1[i];
}
// Let's increment the iterator
// for the smallest value.
if (arr1[i] <= arr2[j] && arr1[i] <= arr3[k]) {
i++;
}
else if (arr2[j] <= arr1[i] && arr2[j] <= arr3[k]) {
j++;
}
else if (arr3[k] <= arr1[i] && arr3[k] <= arr2[j]) {
k++;
}
}
return -1;
};
let v1 = [6, 7, 10, 25, 30, 63, 64];
let v2 = [0, 4, 5, 6, 7, 8, 50];
let v3 = [1, 6, 10, 14];
console.log("Least Common Number: " + findLeastCommonNumber(v1, v2, v3));
I tried to write a function that would find in a multidimensional array (with values from 3 to 7) repeating values for at least 3 times next to each other (vertical and horizontal). And if it finds that, change it for a different value. Let's say 1.
I tried to do this by loops but it doesn't seem to be a good way to solve that or I messed it up. Because for some array it works, for some it does not.
Here's my code:
function searching(array) {
for (i = 0; i < array.length; i++) {
let horizontal = array[i][0];
let howMany = 1;
for (j = 1; j < array[i].length; j++) {
if (horizontal === array[i][j]) {
howMany += 1;
horizontal = array[i][j];
if (howMany >= 3) {
for (d = j; d > j - howMany; d--) {
array[i][d] = 0;
}
}
} else {
horizontal = array[i][j];
howMany = 1;
}
}
}
for (v = 0; v < array.length; v++) {
let vertical = array[0][v];
let howMany = 1;
for (x = 1; x < array.length; x++) {
if (vertical === array[x][v]) {
howMany++;
vertical = array[x][v];
if (howMany >= 3) {
for (d = x; d > x - howMany; d--) {
array[d][v] = 0;
}
}
} else {
vertical = array[x][v];
howMany = 1;
}
}
}
}
The idea is to for example give array:
let array = [
[3, 4, 5, 6, 7],
[3, 4, 5, 6, 7],
[3, 4, 5, 5, 5],
[3, 5, 6, 7, 4]
]
And the result should be:
let result = [
[1, 1, 1, 6, 7],
[1, 1, 1, 6, 7],
[1, 1, 1, 1, 1],
[1, 5, 6, 7, 4]
]
Thanks in advance for any ideas how to solve it :) Greetings!
The problems with your current code are
(1) You're only checking individual rows and columns, when you need to be checking them both (eg, with [[2, 2], [2, 5]], when at the starting position [0][0], you need to look at both [0][1] (and its neighbors, if matching) as well as [1][0] (and its neighbors, if matching).
(2) You're not actually checking for adjacency at the moment, you're just counting up the total number of matching elements in a particular row or column.
Iterate over all indicies of the array. If an index has already been checked, return early. Recursively search for neighbors to that index, and if at least 3 matching in total are found, set them all to 1. Put all matching neighbors in the checked set to avoid checking them again (even if there were less than 2 adjacent matches found total).
setAllAdjacentToOne([
[3, 4, 5, 6, 7],
[3, 4, 5, 6, 7],
[3, 4, 5, 5, 5],
[3, 5, 6, 7, 4]
]);
// all 9s stay, the rest get set to 1:
setAllAdjacentToOne([
[2, 2, 9, 7, 7],
[2, 9, 9, 9, 7],
[3, 4, 4, 5, 5],
[9, 4, 5, 5, 9]
]);
function setAllAdjacentToOne(input) {
const output = input.map(subarr => subarr.slice());
const checked = new Set();
const getKey = (x, y) => `${x}_${y}`;
const width = input[0].length;
const height = input.length;
const getAllAdjacent = (x, y, numToFind, matches = []) => {
if (x >= width || x < 0 || y >= height || y < 0) {
return matches;
}
const key = getKey(x, y);
if (!checked.has(key) && input[y][x] === numToFind) {
checked.add(key);
matches.push({ x, y });
getAllAdjacent(x + 1, y, numToFind, matches);
getAllAdjacent(x - 1, y, numToFind, matches);
getAllAdjacent(x, y + 1, numToFind, matches);
getAllAdjacent(x, y - 1, numToFind, matches);
}
return matches;
};
output.forEach((innerRowArr, y) => {
innerRowArr.forEach((num, x) => {
const allAdjacent = getAllAdjacent(x, y, num);
if (allAdjacent.length <= 2) {
return;
}
allAdjacent.forEach(({ x, y }) => {
output[y][x] = 1;
});
});
});
console.log(JSON.stringify(output));
}
I didn't understand the question at first...
So this is my code:
let array = [
[3, 4, 5, 6, 7],
[3, 4, 5, 6, 7],
[3, 4, 5, 5, 5],
[3, 5, 6, 7, 4]
];
function replace(arr, target = 1) {
let needToChange = []; // save the index to change
const numbers = [3, 4, 5, 6, 7];
const m = arr.length; // m rows
const n = arr[0].length; // n columns
let mi = 0;
let ni = 0;
// search in row
for (mi = 0; mi < m; mi++) {
for (let x = 0; x < numbers.length; x++) {
const num = numbers[x]; // number to search
let counter = 0; // counter for this number in row mi
let tempArr = [];
for (ni = 0; ni < n; ni++) {
const currentNum = arr[mi][ni];
if (currentNum === num) {
counter++;
tempArr.push([mi, ni]);
}
}
if (counter >= 3) {
needToChange = needToChange.concat(tempArr);
}
}
}
// search in column
for (ni = 0; ni < n; ni++) {
for (let x = 0; x < numbers.length; x++) {
const num = numbers[x]; // number to search
let counter = 0; // counter for this number in row mi
let tempArr = [];
for (mi = 0; mi < m; mi++) {
const currentNum = arr[mi][ni];
if (currentNum === num) {
counter++;
tempArr.push([mi, ni]);
}
}
if (counter >= 3) {
needToChange = needToChange.concat(tempArr);
}
}
}
// replace
needToChange.forEach(([i, j]) => {
array[i][j] = target;
});
}
replace(array);
array.forEach(row => {
console.log(row.join(', '));
})
I am trying to write a function that prints only the even numbers from a nested array. Here is my attempt and it keeps returning "undefined".
function printEvents(){
var nestedArr = [[1,2,3],[4,5,6],[7,8],[9,10,11,12]];
for (var i = 0; i<nestedArr.length; i++) {
for (var j = 0; j<nestedArr[i]; j++) {
var evenNumbers = nestedArr[i][j]
}
}
if (evenNumbers % 2 == 0) {
console.log(evenNumbers)
}
}
printEvents();
You could use a recursive approach, if the item is an array. You need to move the test for evenness inside of the for loop.
function printEvents(array) {
var i;
for (i = 0; i < array.length; i++) {
if (Array.isArray(array[i])) {
printEvents(array[i]);
continue;
}
if (array[i] % 2 == 0) {
console.log(array[i]);
}
}
}
printEvents([[1, 2, 3], [4, 5, 6], [7, 8], [9, 10, 11, 12], [[[13, [14]]]]]);
Solution with a callback
function getEven(a) {
if (Array.isArray(a)) {
a.forEach(getEven);
return;
}
if (a % 2 == 0) {
console.log(a);
}
}
getEven([[1, 2, 3], [4, 5, 6], [7, 8], [9, 10, 11, 12], [[[13, [14]]]]]);
You just have a couple issues. You need to check the length of your nested arrays and you need to move your code that checks whether a number is even or not inside the array.
var nestedArr = [[1,2,3],[4,5,6],[7,8],[9,10,11,12]];
for (var i = 0; i<nestedArr.length; i++) {
for (var j = 0; j<nestedArr[i].length; j++) {
var evenNumbers = nestedArr[i][j]
if (evenNumbers % 2 == 0) {
console.log(evenNumbers)
}
}
}
You can do this more easy using filter method which accepts a callback method.
var nestedArr = [[1,2,3],[4,5,6],[7,8],[9,10,11,12]];
var mergedArray=[].concat.apply([], nestedArr);
console.log(mergedArray.filter(a => a%2 == 0));
You can use reduce with every for something quick.
var nestedArr = [ [1, 2, 3],[4, 5, 6],[7, 8],[9, 10, 11, 12]];
var sift = nestedArr.reduce(function(r,o) {
o.every(i => i % 2 === 0 ? r.push(i) : true)
return r;
}, []);
console.log(sift);
If you want a one-liner you can use ReduceRight with Filter
var nestedArr = [[1, 2, 3],[4, 5, 6],[7, 8],[9, 10, 11, 12]];
var sift = nestedArr.reduceRight((p, b) => p.concat(b).filter(x => x % 2 === 0), []);
console.log(sift)