How to change every 2nd and 3rd element in an array? - javascript

Here is my dilemma, I have this code:
var fibs = [1, 2];
for (i = 0; i < (window.innerWidth / 50); i++) {
if (fibs.length < 15) {
var n = fibs[i] + fibs[i + 1];
fibs.push(n);
}
}
Which creates an array of the Fibonacci Sequence. I then copy the contents of fibs to a new array, top. What I'm having trouble with is that I need every 2nd and 3rd elements on top to be the inverse sign of fibs. ie. top[0] = fibs[0]; top[1] = -fibs[1]; top[2] = -fibs[2]; top[3] = fibs[3] I want to be able to do this programmatically because the lengths of the arrays will change based on the width of the screen.
Below is what I'm trying to use.
var top = [];
for (i = 0; i < fibs.length; i++) {
if (i % 2 == 0 || i % 3 == 0) {
top[i] = -(fibs[i]);
} else {
top[i] = fibs[i];
}
}
What I get is [-1, 2,-3, -5, -8, 13, -21, 34, -55, -89, -144, 233, -377, 610, -987], when what I'm looking for is [-1, 2, 3, -5, -8, 13, 21, -34, -55, 89, 144, ... ].

While this might not be the best way to do it, this ought to work:
var topp = [];
for (i = 0; i < fibs.length; i++) {
if (i % 3 == 1 || i % 3 == 2) {
topp[i] = fibs[i] - (2 * fibs[i]);
} else {
topp[i] = fibs[i];
}
}
The code is pretty self explanatory.
EDIT:
Thanks to #alexpods for the tip about the top keyword.
Thanks to #Scott Hunter for pointing out the logical error.

Allow me to throw in a bit more flexible (and readable) solution to your problem:
function fibonacci(length) {
var out = [0, 1];
for (i = 2; i < length; i++) {
out[i] = out[i-1] + out[i-2];
}
return out;
}
function modifyValues(source, factors) {
var out = [];
for (i = 0; i < source.length; i++) {
out[i] = source[i] * factors[i % factors.length];
}
return out;
}
fibs = fibonacci(15);
mod1 = modifyValues(fibs, [1,-1,-1]);
console.log(fibs, mod1);
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
[0, -1, -1, 2, -3, -5, 8, -13, -21, 34, -55, -89, 144, -233, -377]

I believe you want
if ( i%3==1 || i%3==2 ) {
Alternatively, you could use
if ( i%3 ) { // will be treated as true as long as i%3 != 0

Related

Reset counter when not in range and sum progression

I have an arrays of numbers, and specified range if sequence continues (range rule was met between two numbers) then i add value to result and increase counter by one, else i reset the counter and add nothing to result on this step. Better show in an example:
const numbers = [1, 4, 5, 6, 7, 33, 44, 46]; // they are always going to be from smallest to bigger
const progress = [0, 10, 20, 30, 40, 50, 60]; // 70, 80, etc
let res = 0;
for (let i = 1, j = 0; i < numbers.length; i++) {
const range = numbers[i] - numbers[i - 1];
if (range <= 5) {
j += 1;
res += progress[j];
} else {
j = 0;
}
}
res; // 110
Is there better way to approach this problem?
Well, by looking at your code & the explanation you gave, I think you have incremented 'j' before you added progress for 'j'. that portion should be like following...
if (range <= 5) {
res += progress[j];
j += 1;
}
You have asked for a better approach. But it would help if you specified from which perspective/scenario you are looking for a better approach.
you can do the same with reduce method
const numbers = [1, 4, 5, 6, 7, 33, 44, 46]; // they are always going to be from smallest to bigger
const progress = [0, 10, 20, 30, 40, 50, 60]; // 70, 80, etc
let resp = 0;
const result = numbers.reduce((acc, rec, i, arr) => {
if (rec - arr[i - 1] <= 5) {
resp += 1;
acc = acc + progress[resp];
return acc;
}
resp = 0;
return acc;
}, 0);
result;
You can read more about reduce here
Hope it answers your question.
Happy coding!

How to get single even or odd number from a array in simplest way?

To get single even or odd number from a array. Example:
[20, 10, 11, 200, 30] => will return 11.
[31, 23, 45, 20, 43] => will return 20.
I have tried the below function to achieve this requirement:
function getEvenOrOddNum(arr) {
var checkVal, num, i, len = arr.length;
if (len > 2) {
for (i = 0; i < 3; i++) {
var mod = arr[i] % 2;
if (checkVal == mod) { break; }
checkVal = mod;
}
checkVal = checkVal == 0 ? 1 : 0;
num = arr.filter((val) => val % 2 == checkVal);
num = num.length < 2 ? num[0] : null;
}
return num || null;
}
console.log(getEvenOrOddNum([20, 10, 11, 200, 30])) //=> return 11
console.log(getEvenOrOddNum([31, 23, 45, 20, 43])) //=> return 20
console.log(getEvenOrOddNum([20, 10])) //=> return null
console.log(getEvenOrOddNum([20, 10, 11, 23, 200, 30])) //=> return null
console.log(getEvenOrOddNum([31, 23, 45, 20, 43, 50])) //=> return null
You could coount the types and store the last value of each type and check if two values have the same type, then take a flag for the wanted type.
Return then the type with the flag and check the count before.
l means the left element and r the right one which is the actual item as well. Bothe elements are neede for comparing both types of even/odd.
For example:
vv actual item
[20, 10, 11, 200, 30] array
l r variables
function getSingle(array) {
var count = [0, 0],
values = [],
flag;
array.forEach((r, i, { length, [(length + i - 1) % length]: l }) => {
if (l % 2 === r % 2) flag = 1 - r % 2;
values[r % 2] = r;
count[r % 2]++;
});
return count[flag] === 1
? values[flag]
: null;
}
console.log(getSingle([20, 10, 11, 200, 30])); // 11
console.log(getSingle([31, 23, 45, 20, 43])); // 20
console.log(getSingle([20, 10])); // null
console.log(getSingle([20, 10, 11, 23, 200, 30])); // null
console.log(getSingle([31, 23, 45, 20, 43, 50])); // null
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use Array.prototype.find().
This will call the provided function for each array element, and will return the first element which 'match the condition' (the provided function returns true for):
const evenArray=[2,4,6,8,9]
const oddArray=[1,2,3,5,7,9]
console.log(evenArray.find(element=> element % 2 === 1)) //9
console.log(oddArray.find(element=> element % 2 === 0)) //2

compare element in two arrays

I want to compare each element of array real with each element of array number. And if there is any matches push them in array add so I can see them. In this case add must be 2,3,6,10,14 if code is good.
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script>
var real=[1,2,3,4,5,6,7,8,10,14,16,233,235,245,2,5,7,236,237];
var number=[2,3,6,10,12,13,14,172,122,234];
var add=[];
for (k=0; k<number.length; k++)
{
for (w=0; w<real.length; w++)
{
if (number[k]==real[w]);
{
add.push(number[k],real[w]);
}
};
};
document.write(add+"<br>");
</script>
Here is a short and simple solution using Array.forEach and Array.indexOf functions:
var real = [1,2,3,4,5,6,7,8,10,14,16,233,235,245,2,5,7,236,237],
number = [2,3,6,10,12,13,14,172,122,234],
add = [];
real.forEach(function(v) {
if (number.indexOf(v) !== -1 && this.indexOf(v) === -1) this.push(v);
}, add);
console.log(add); // [2, 3, 6, 10, 14]
A more elegant and readable solution:
var matched = [];
real.forEach(function(realNum) {
number.forEach(function(numberNum) {
if(realNum == numberNum && matched.indexOf(realNum) === -1) {
matched.push(realNum);
}
});
});
Here's one way you can do it using ES6:
var real = [1, 2, 3, 4, 5, 6, 7, 8, 10, 14, 16, 233, 235, 245, 2, 5, 7, 236, 237];
var number = [2, 3, 6, 10, 12, 13, 14, 172, 122, 234];
var filtered = real.filter(x => number.indexOf(x) > -1);
var unique = new Set(filtered);
document.body.innerHTML = [...unique];
try this way...
Sorted the main Array, removed duplicates and the find the common elements from both the arrays.
var main = [1,2,3,4,5,6,7,8,10,14,16,233,235,245,2,5,7,236,237];
var compare = [2,3,6,10,12,13,14,172,122,234];
function compareNumbers(a, b) {
return a - b;
}
console.log('Sorted Array :', main.sort(compareNumbers) );
// Sorted Array : [1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 8, 10, 14, 16, 233, 235, 236, 237, 245]
Array.prototype.unique = function() {
var unique = [];
for (var i = 0; i < this.length; i++) {
var current = this[i];
if (unique.indexOf(current) < 0) unique.push(current);
}
return unique;
}
console.log('Unique Array Elements:', main.unique() );
// Unique Array Elements: [1, 2, 3, 4, 5, 6, 7, 8, 10, 14, 16, 233, 235, 236, 237, 245]
function commonElements(arr1, arr2) {
var common = [];
for (var i = 0; i < arr1.length; i++) {
for (var j = 0; j < arr2.length; j++) {
if (arr1[i] == arr2[j] ) {
common.push( arr1[i] );
j == arr2.length; // To break the loop;
}
}
}
return common;
}
console.log('Common Elements from Both Arrays : ', commonElements(main.unique(), compare.unique()) );
//Common Elements from Both Arrays : [2, 3, 6, 10, 14]

Put the 0 at the same position on two arrays in javascript

Sorry for my english, really really tired...
Imagine two arrays:
var a = [-10, 5, 0, 5, 10, 15];
var b = [-20, 0, 20, 40, 60, 80];
Every value of each array increments by the same number (a by 5 and b by 20).
I would like to find a way to put the zero on the same position on both arrays.
Keep in mind that:
each serie has the same number of elements
you can change the min and max value of each array (the original min and max of a serie have to be a part of the serie, check the example below)
each value of the array increments by the same number, you can change this value
The expected result could be something like
var a = [-10, 5, **0**, 5, 10, 15];
var b = [-60, -30, **0**, 30, 60, 90];
b increments now by 30 and the original min (-20) and max (8) values are included in the interval.
Any idea on how to do that using javascript?
Why I'd like to do that? To solve something like that:
http://peltiertech.com/Excel/Charts/AlignXon2Ys.html
Thanks in advance
Rob
The following is a result of code iteration based on comments. Previous code has been removed for clarity, but remains available in edit history.
This one fixes the zero in the middle of a series, then adjusts values according to the initial requirements. Also rounding to the nearest 5 (previous code was inadequate in that regard, sorry). HTH.
function develop(data) {
if (data.length < 3) {
return data;
}
var lower = data[0];
var upper = data[data.length - 1];
var index = (data.length - 1) / 2;
var numLeft = Math.floor(index);
var numRight = Math.ceil(index);
var leftStep = findStep(lower, numLeft, false);
var rightStep = findStep(upper, numRight, true);
var step = roundStep(Math.max(leftStep, rightStep), 5);
var result = [];
for (var ii = 0; ii < data.length; ii++) {
result[ii] = step * (ii - numLeft);
}
return result;
// ---
function findStep(boundary, numEntries, positive) {
if (positive && boundary <= 0 || !positive && boundary >= 0) {
return 1;
}
return Math.abs(Math.ceil(boundary / numEntries));
}
function roundStep(step, roundTo) {
if (step < roundTo) {
return step;
}
return Math.ceil(step / roundTo) * roundTo;
}
}
function test() {
var testData = [
[-10, -5, 0, 5, 10, 15],
[-20, 0, 20, 40, 60, 80],
[0, 72, 144, 216, 288, 360],
[-30, -25, -20, -15, -10, 0]
];
var results = [];
for (var ii = 0; ii < testData.length; ii++) {
var data = testData[ii];
results.push(JSON.stringify(data) + " => " + develop(data));
}
document.getElementById("results").innerHTML = results.join("<br>");
}
<input type="button" value="test()" onclick="test()" />
<div id="results"></div>
This seems to work, but I'm probably doing couple thins not necessary
var setIndex = function (arr1, arr2) {
var arr1Min = arr1[0];
var arr2Min = arr2[0];
var arr1Max = arr1[arr1.length-1];
var arr2Max = arr2[arr2.length-1];
var length = arr1.length;
var newRatio;
var newMin;
var newMax;
var ratioArr1 = arr1Max/arr1Min;
var ratioArr2 = arr2Max/arr2Min;
if(ratioArr1 < ratioArr2){
newMin = calcNewMin(arr1Min, arr1Max, ratioArr2);
newMax = ratioArr2 * newMin;
newRatio = (newMax - newMin)/(length-1);
arr1 = [];
for(var i = 0; i < length; i++){
arr1.push(newMin + (i * newRatio));
}
return [arr1, arr2];
} else {
newMin = calcNewMin(arr2Min, arr2Max, ratioArr1);
newMax = ratioArr1 * newMin;
newRatio = (newMax - newMin)/(length-1);
arr2 = [];
for(var i = 0; i < length; i++){
arr2.push(newMin + (i * newRatio));
}
return [arr1, arr2];
}
};
var calcNewMin = function(min, max, ratio){
var count = 1;
var newMin = min;
var newMax = max;
while(newMax <= max){
count++;
newMin = min - count;
newMax = newMin * ratio;
}
return newMin;
};
Compare the array ranges first, if one array includes the range of the other one then the answer is a series with an increment higher than the array that increments where the start of the range is:
0 - increment * (position of 0)
In your example:
var a = [-10, 5, 0, 5, 10, 15];
var b = [-20, 0, 20, 40, 60, 80];
b includes the range of a, so any series that have an increment higher than b and obeys the rules at the beginning are valid solutions to the problem:
[-20, 0, 20, 40, 60, 80]
[-21, 0, 21, 42, 63, 84]
[-22, 0, 22, 44, 66, 88]
...
[-60, 0, 60, 120, 180, 240]
All of these series include the range of a.
It gets a little bit trickier when the ranges overlap:
[-10, 0, 10, 20, 30]
[ 0, 20, 40, 60, 80]
The idea behind is the same. We'll pick the series with the smallest value:
[-10, 0, 10, 20, 30]
From this, we'll need to find a higher increment so that it satisfies:
start + (inc * length) > max of other series.
Where start:
0 - (inc * pos of 0 in picked series)
Moving stuff around you get:
inc > (max value / (length - pos of 0 in picked series))
So in this example:
inc > 80 / (5 - 2)
inc > 80 / 3
inc > 26.666
Lets try it with an increment of 27 and a start of -27:
[-27, 0, 27, 54, 81]
Now, that you we know how to solve the problem, lets try it with code:
function getMinMax(a, b){
var last = a.length - 1,
min = a[0] < b[0] ? a : b,
max = a[last] > b[last] ? a : b;
return { min : min, max : max };
}
function closestInc(range){
if(range.min === range.max){
return range.min[1] - range.min[0];
} else {
var last = range.min.length - 1,
maxValue = range.max[last],
posOfCero = range.min.indexOf(0);
return (maxValue/(range.min.length - posOfCero));
}
}
So, all the possible answers would be any series with an increment value bigger than closestInc(a, b) and a start of -closestInc(a,b) * posOfCero.
Here's a function that prints out all possible values slowly:
function createSeries(inc, posOfCero, count) {
var series = [],
start = -(inc * posOfCero);
for (var i = 0; i < count; i++) {
series.push(start + (inc * i));
}
return series;
}
var a = [-10, 5, 0, 5, 10, 15],
b = [-20, 0, 20, 40, 60, 80],
ranges = getMinMax(a, b),
inc = closestInc(ranges),
posOfCero = ranges.min.indexOf(0);
setTimeout(function printSeries(i) {
console.log(createSeries(inc + 1, posOfCero, range.min.length));
setTimeout(printSeries, 1000, i + 1);
}, 1000, 1);
A snippet below:
function getMinMax(a, b) {
var last = a.length - 1,
min = a[0] < b[0] ? a : b,
max = a[last] > b[last] ? a : b;
return {
min: min,
max: max
};
}
function closestInc(range) {
if (range.min === range.max) {
return range.min[1] - range.min[0];
} else {
var last = range.min.length - 1,
maxValue = range.max[last],
posOfCero = range.min.indexOf(0) + 1;
return (maxValue / (range.min.length - posOfCero));
}
}
function createSeries(inc, posOfCero, count) {
var series = [],
start = -(inc * posOfCero);
for (var i = 0; i < count; i++) {
series.push(start + (inc * i));
}
return series;
}
//var a = [-10, 5, 0, 5, 10, 15],
// b = [-20, 0, 20, 40, 60, 80],
var a = [-10, 0, 10, 20, 30],
b = [ 0, 20, 40, 60, 80],
ranges = getMinMax(a, b),
inc = closestInc(ranges),
posOfCero = ranges.min.indexOf(0);
setTimeout(function printSeries(i) {
console.log(createSeries(Math.round(inc + i), posOfCero, ranges.min.length));
setTimeout(printSeries, 1000, i + 1);
}, 1000, 1);
A last note, this aren't all possible series to match your rules (there might still be some valid increments between the series).
You can use Array.push and Array.unshift like this jsFiddle

Spiral traversal of a matrix - recursive solution in JavaScript

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);
};

Categories