I am trying to figure out how to get a window of positions based off of a center point in JavaScript.
Say I have the following array:
[0,1,2,3,4,5,6,7,8,9]
If I wanted to get position 5 as the centre point, I'd also like to get positions 3,4,6 and 7 which would produce [3,4,5,6,7].
However, I'm also trying to get it where when it's within reach of the left or right bound of the array, it pushes the window in the opposite direction.
For example if I wanted position 1 I'd like the array to return as [0,1,2,3,4].
Or if I wanted position 0, I'd like the array as [0,1,2,3,4].
The same applies for the end of the array, e.g. I want to get position 8 so I'd return [5,6,7,8,9].
I'm struggling to get this into JavaScript and I feel I am overcomplicating the matter.
My current code is as follows however I am not attached to this code at all so completely changing it is fine:
positions() {
let left = 0;
let right = 5;
let middle = this.steps.length / 2;
// Closer to left.
if (this.index < middle) {
if (this.steps[this.index - 2]) {
left = this.index - 2;
right = this.index + 2;
}
else if (this.steps[this.index - 1]) {
left = this.index - 1;
right = this.index + 3;
}
}
// Closer to right.
else if (this.index > middle) {
if (this.steps[this.index + 3]) {
left = this.index - 2;
right = this.index + 3;
}
else if (this.steps[this.index + 2]) {
left = this.index - 3;
right = this.index + 2;
}
else if (this.steps[this.index + 1]) {
left = this.index - 4;
right = this.index + 1;
}
}
else {
left = this.index - 2;
right = this.index + 3;
}
return { left, right };
},
You can employ some maths to calculate what you get.
The start index is the centre point minus the span you want. E.g, centre 5 and span of 2 produces a start position of 5 - 2 = 3.
The end index is the centre point plus the span. E.g., centre 5 and span of 2 produces an end position of 5 + 2 = 7.
[0,1,2,3,4,5,6,7,8,9]
^ ^ ^
| | |
start -+ | |
centre +---+ |
end ---+-------+
| |
[3,4,5,6,7]
To handle the "overflow" you can clamp the start/end values.
start cannot be lower than the start of the array (which is 0).
while end can be at most the last position of the array (which is arr.length - 1).
If the start or end position forms less span than the expected one, the leftover can be transferred to the opposite index:
With centre of 1 and span of 2:
[0,1,2,3,4,5,6,7,8,9]
^ ^ ^
| | |
start --+ | |
centre ---+ |
padded end -----+
With centre of 8 and span of 2:
[0,1,2,3,4,5,6,7,8,9]
^ ^ ^
| | |
padded start -----+ | |
centre -----------------+ |
end ----------------------+
Finally, the only situation left to handle is what happens if the array is simply not big enough. That's up to you, just returning the whole array is sensible but also you may choose to throw an error or an empty array. If you are sure it's never going to happen, you can also leave it unhandled.
Here is how the implementation can look
function getRange(arr, pos, span) {
if ((span*2 + 1) > arr.length) {
throw Error("not enough items in array"); //or just return arr; or return []; etc.
}
let start = Math.max((pos - span), 0);
let end = Math.min((pos + span), arr.length - 1);
const leftoverStart = span - (pos - start);
const leftoverEnd = span - (end - pos);
if (leftoverStart) {
end += leftoverStart;
} else if (leftoverEnd) {
start -= leftoverEnd;
}
return arr.slice(start, end+1);
}
const arr = [0,1,2,3,4,5,6,7,8,9];
console.log(getRange(arr, 5, 2));
console.log(getRange(arr, 1, 2));
console.log(getRange(arr, 8, 2));
let array = [0,1,2,3,4,5,6,7,8,9]
const getSlice = (arr,index) => {
let leftSide = index - 2;
let rightSide = index + 2;
if(leftSide<0){
return arr.slice(0,5)
}
if(rightSide >= arr.length){
return arr.slice(arr.length-5)
}
return arr.slice(index-2,index+3)
}
console.log(getSlice(array,0))
console.log(getSlice(array,1))
console.log(getSlice(array,2))
console.log(getSlice(array,4))
console.log(getSlice(array,7))
console.log(getSlice(array,8))
console.log(getSlice(array,9))
var steps = [0,1,2,3,4,5,6,7,8,9];
var res = [];
var i = 3; // index // or 0, 9, 8, 2, 1
var mid = steps.length / 2;
var lenghOfResult = 5;
var left = 0;
var right = 0;
function computed() {
res = [];
res.push(i);
left = i;
right = i;
for(let c=1;c<=lenghOfResult;c++) {
if(steps.hasOwnProperty(i-c) && (i-c <= i-1) && res.length <lenghOfResult)
{
left = i-c;
res.unshift((i-c));
}
if(steps.hasOwnProperty(i+c) && (i+c >= i) && res.length <lenghOfResult) {
right = i+c;
res.push((i+c));
}
}
console.log('left', left, 'right', right);
}
computed();
console.log(res);
Related
I am trying to solve a question on Leetcode
Find Smallest Letter Greater Than Target
Given a characters array letters that is sorted in non-decreasing order and a character target, return the smallest character in the array that is larger than target.
Note that the letters wrap around.
For example, if target == 'z' and letters == ['a', 'b'], the answer is 'a'.
Example 1:
Input: letters = ["c","f","j"], target = "a"
Output: "c"
Example 2:
Input: letters = ["c","f","j"], target = "c"
Output: "f"
Example 3:
Input: letters = ["c","f","j"], target = "d"
Output: "f"
Constraints:
2 <= letters.length <= 104
letters[i] is a lowercase English letter.
letters is sorted in non-decreasing order.
letters contains at least two different characters.
target is a lowercase English letter.
My solution until now looks something like this:
function nextGreatestAlphabet(letters, target){
let left = 0;
let right = letters.length - 1;
let res = -1;
while(left <= right) {
let mid = Math.floor(left + (right - left) / 2);
if (letters[mid] == target ) { console.log(1)
res = letters[mid];
left = mid + 1;
}
if(letters[mid] > target){ console.log(2)
right = mid -1;
res = letters[mid];
}
if(letters[mid] < target ){ console.log(3)
left = mid + 1;
}
}
return res;
}
console.log(nextGreatestAlphabet(["c","f","j"],"c")); //c
But the correct result should be "f" as c is already present here and next greatest is f
You should avoid setting res during the search, and certainly not set it when letters[mid] == target, as it is sure that is not the correct answer. Instead you should get the result after the loop has exited.
This then also means you don't need a separate case for letters[mid] == target, as the rest of the action is exactly what is done for letters[mid] < target. So you can make this a quite simple if (letters[mid] > target) ... else ... structure.
After the loop has exited, the left index points to the desired character. Then you need to deal with the boundary case where that index points beyond the array and map it to 0. This you can do with the remainder operator:
NB: I use here the name of the function as required by the LeetCode challenge:
function nextGreatestLetter(letters, target) {
let left = 0;
let right = letters.length - 1;
while(left <= right) {
let mid = Math.floor(left + (right - left) / 2);
if(letters[mid] > target) {
right = mid -1;
} else {
left = mid + 1;
}
}
return letters[left % letters.length];
}
Personally, I prefer working with right being the index just after the range that is under consideration, and for deriving mid you can use the >> operator (Array sizes are limited in this challenge, so that is a safe operation):
function nextGreatestLetter(letters, target) {
let left = 0;
let right = letters.length;
while (left < right) {
let mid = (left + right) >> 1;
if (letters[mid] > target) {
right = mid;
} else {
left = mid + 1;
}
}
return letters[left % letters.length];
}
I think that your
if (letters[mid] == target ) { console.log(1)
res = letters[mid];
left = mid + 1;
}
sets the result to the target but you actually want to have the next one, so it should be sth. like
res=letter[mid+1].
if
mid==letters.length - 1
holds then it is already the right end. then you can just put
res = letter[0];
Question
How can i compare two adjacent cells thanks to them coordinates?
Documentation which helped me
I already saw these questions, they helped me but they are different from my case:
question on stackOverflow
question on stackOverflow
question on stackOverflow
Mds documentation to build a dynamic table
Code
I've a dynamically generated table
function tableGenerate(Mytable){
for(var i = 0; i < myTable.length; i++) {
var innerArrayLength = myTable[i].length;
for(var j = 0; j<innerArrayLength; j++){
if(myTable[i][j] === 0){
myTable[i][j]="x";
}else{
myTable[i][j]="y";
};
};
$("#aTable").append("<tr><td>"+ myTable[i].join('</td><td>') + "</td></tr>")
}
}
About the interested cells (two global variables) in actualPosition row and cell have random values
var mainTd = {
name: 'interestedValue',
actualPosition:{
row: 5,
cell: 4
}
};
var otherMainTd = {
actualPosition:{
row: 2,
cell: 3
}
};
The final part of the code works in this way:
I save the position of selectedTd in two differents variables
I create the 2d array directions with the coordinates of near cells relatives to the selectedTd
enter in the first if, compare the two cells. If one of the coordinates are the same, you enter in this last if.
function compare(selectedTd) {
let tdRow = selectedTd.actualPosition.row;
let tdCell = selectedTd.actualPosition.cell;
let directions = [
[tdRow - 1, tdCell],
[tdRow + 1, tdCell],
[tdRow, tdCell + 1],
[tdRow, tdCell - 1]
]; //these are the TD near the mainTd, the one i need to compare to the others
let tdToCompare = [];
if (selectedTd.name === 'interestedValue') {
tdToCompare = [otherMainTd.actualPosition.row, otherMainTd.actualPosition.cell];
for (let i = 0; i < directions.length; i++) {
if (directions[i] == tdToCompare) {
console.log('you are here');
}
}
} else {
tdToCompare = [mainTd.actualPosition.row, mainTd.actualPosition.cell];
for (let i = 0; i < directions.length; i++) {
if (directions[i] === tdToCompare) {
console.log('you are here');
}
}
}
};
Now the main problem is: I read the coordinates, I store them in the 2 arrays, I can read them but I cannot able to enter in the if statement.
This is what I want to achieve: compare the coordinates of the blackTd with the coordinates of the red-borders td.
Codepen
the interested functions in the codepen are with different names, but the structure is the same that you saw in this post. I changed the original names because I think it could be more clear with general names instead of the names that i choose.
the interested functions are:
function fight(playerInFight) ---> function compare(selectedTd)
function mapGenerate(map) ---> function tableGenerate(MyTable)
mainTd and otherMainTd ---> character and characterTwo
CodepenHere
Update:
Reading your code again I think I figured out the problem. You're comparing array instances instead of their actual values. See this simple example to illustrate the issue:
var a = [1];
var b = [1];
console.log(a===b);
What you'd need to do in your code is this:
if (selectedTd.name === 'interestedValue') {
tdToCompare = [otherMainTd.actualPosition.row, otherMainTd.actualPosition.cell];
for (let i = 0; i < directions.length; i++) {
if (
directions[i][0] === tdToCompare[0] &&
directions[i][1] === tdToCompare[1]
) {
console.log('you are here');
}
}
} else {
tdToCompare = [mainTd.actualPosition.row, mainTd.actualPosition.cell];
for (let i = 0; i < directions.length; i++) {
if (
directions[i][0] === tdToCompare[0] &&
directions[i][1] === tdToCompare[1]
) {
console.log('you are here');
}
}
}
Now it checks if the values, and thus the cells, are matching.
Recommendations:
If I were you, I would write the method a bit different. Below is how I would do it.
function compare(selectedTd) {
const
// Use destructuring assignemnt to get the row and cell. Since these are
// values that won't be changed in the method declare them as "const". Also
// drop the "td" prefix, it doesn't add anything useful to the name.
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
{ row, cell } = selectedTd.actualPosition,
// Directions can also be a const, it will not be reassigned in the method.
directions = [
[row - 1, cell],
[row + 1, cell],
[row, cell + 1],
[row, cell - 1]
],
// A few things happens in this line:
// - It is a destructuring assignment where the names are changed. In this case
// row and cell are already assigned so it is necessary to give them another name.
// - Don't put the row and cell in an array. You will have to access the actual values
// anyway as you can't compare the array instances.
// - Instead of doing this in the if…else you had, decide here which cell you want to
// look for. It means the rest of the method can be written without wrapping any
// logic in an if…else making it less complex.
{ row: referenceRow, cell: referenceCell } = (selectedTd.name === 'interestedValue')
? otherMainTd.actualPosition
: mainTd.actualPosition,
// Use find instead of a for loop. The find will stop as soon as it finds a match. The
// for loop you had kept evaluating direction items even if the first one was already
// a match.
// The "([row,cell])" is the signature of the callback method for the find. This too is
// a destructuring assignment only this time with one of the arrays of the directions
// array. The first array item will be named "row" and the second item "cell". These
// variable names don't clash with those declared at the top of this method as this
// is a new scope.
// The current directions entry is a match when the row and cell values match.
matchingNeighbor = directions.find(([row, cell]) => row === referenceRow && cell === referenceCell);
// "find" returns undefined when no match was found. So when match is NOT unddefined
// it means directions contained the cell you were looking for.
if (matchingNeighbor !== undefined) {
console.log('you are here');
}
};
const
mainTd = {
name: 'interestedValue',
actualPosition: {
cell: 1,
row: 1
}
},
otherMainTd = {
actualPosition: {
cell: 0,
row: 1
}
};
compare(mainTd);
Orginal answer:
There is quite a bit going on in your question, I hope I understood it properly.
What I've done is create a Grid, you pass this the dimensions and it will create the array for each cell in the grid. Then it returns an object with some methods you can use to interact with the grid. It has the following methods:
cellAtCoordinate: Pass it an X and Y coordinate and it returns the cell.
isSameLocation: Pass it two cells and it checks if the cells are in the same location.
neighborsForCoordinate: Pass it an X and Y coordinate and it returns an array with the cells above, below, to the right, and to the left (if they exist).
With all of this out of the way the compare method becomes a little more manageable. Getting the neighbours is now just a single call, the same for the check if two cells match.
Like I said, I hope this is what you were trying to achieve. If I got the problem wrong and something needs some further explaining, please let me know.
/**
* Creates grid with the provided dimensions. The cell at the top left corner
* is at coordinate (0,0). The method returns an object with the following
* three methods:
* - cellAtCoordinate
* - isSameLocation
* - neighborsForCoordinate
*/
function Grid(width, height) {
if (width === 0 || height === 0) {
throw 'Invalid grid size';
}
const
// Create an array, each item will represent a cell. The cells in the
// array are laid out per row.
cells = Array.from(Array(width * height), (value, index) => ({
x: index % width,
y: Math.floor(index / height)
}));
function cellAtCoordinate(x, y) {
// Make sure we don't consider invalid coordinate
if (x >= width || y >= height || x < 0 || y < 0) {
return null;
}
// To get the cell at the coordinate we need to calculate the Y offset
// by multiplying the Y coordinate with the width, these are the cells
// to "skip" in order to get to the right row.
return cells[(y * width) + x];
}
function isSameLocation(cellA, cellB) {
return (
cellA.x === cellB.x &&
cellA.y === cellB.y
);
}
function neighborsForCoordinate(x, y) {
// Make sure we don't consider invalid coordinate
if (x >= width || y >= height || x < 0 || y < 0) {
return null;
}
const
result = [];
// Check if there is a cell above.
if (y > 0) result.push(cellAtCoordinate(x, y - 1));
// Check if there is a cel to the right
if (x < width) result.push(cellAtCoordinate(x + 1, y));
// Check if there is a cell below.
if (y < height) result.push(cellAtCoordinate(x, y + 1));
// Check if there is a cell to the left.
if (x > 0) result.push(cellAtCoordinate(x - 1, y));
return result;
}
return {
cellAtCoordinate,
isSameLocation,
neighborsForCoordinate
}
}
function compareCells(grid, selectedCell) {
const
// Get the neighbors for the selected cell.
neighbors = grid.neighborsForCoordinate(selectedCell.x, selectedCell.y);
compareAgainst = (selectedCell.name === 'interestedValue')
? otherMainTd
: mainTd;
// In the neighbors, find the cell with the same location as the cell
// we want to find.
const
match = neighbors.find(neighbor => grid.isSameLocation(neighbor, compareAgainst));
// When match is NOT undefined it means the compareAgainst cell is
// a neighbor of the selected cell.
if (match !== undefined) {
console.log(`You are there at (${match.x},${match.y})`);
} else {
console.log('You are not there yet');
}
}
// Create a grid which is 3 by 3.
const
myGrid = Grid(3, 3),
// Place the main TD here:
// - | X | -
// - | - | -
// - | - | -
mainTd = {
name: 'interestedValue',
x: 1,
y: 0
},
// Place the other TD here:
// - | - | -
// Y | - | -
// - | - | -
otherMainTd = {
x: 0,
y: 1
};
// Check if the mainTd is in a cell next to the otherMainTd. It is not
// as the neighboring cells are:
// N | X | N
// Y | N | -
// - | - | -
compareCells(myGrid, mainTd);
// Move the mainTd to the center of the grid
// - | - | -
// Y | X | -
// - | - | -
mainTd.y = 1;
// Compare again, now the main TD is next the the other.
// - | N | -
// YN | X | N
// - | N | -
compareCells(myGrid, mainTd);
This question already has answers here:
Get the closest number out of an array
(21 answers)
Closed 7 years ago.
I have an ordered array:
btnDrag.pos = [0, 65, 131, 196, 259, 323, 388, 453, 517];
And a function that fires when drag stops:
btnDrag.draggable({
axis: 'x',
containment: 'parent',
stop: function() {
var index = (function(){
var new_x = btnDrag.position().left;
// now, how to find the closest index in btnDrag.pos relative to new_x ?
// return index;
})();
btnDrag.animate({
'left': (btnDrag.pos[index] + 'px')
});
}
});
The array values are points which btnDrag is allowed to stay (in axis 'x').
So, the function must return the closest index with the value to btnDrag go.
Thanks in advance.
Since your array is sorted, the fastest way is to use a modified version of the binary search algorithm:
function closest (arr, x) {
/* lb is the lower bound and ub the upper bound defining a subarray or arr. */
var lb = 0,
ub = arr.length - 1;
/* We loop as long as x is in inside our subarray and the length of our subarray is
greater than 0 (lb < ub). */
while (ub - lb > 1) {
var m = parseInt((ub - lb + 1) / 2); // The middle value
/* Depending on the middle value of our subarray, we update the bound. */
if (arr[lb + m] > x) {
ub = lb + m;
}
else if (arr[lb + m] < x) {
lb = lb + m;
}
else {
ub = lb + m;
lb = lb + m;
}
}
/* After the loop, we know that the closest value is either the one at the lower or
upper bound (may be the same if x is in arr). */
var clst = lb;
if (abs(arr[lb] - x) > abs(arr[ub] - x)) {
clst = ub;
}
return clst; // If you want the value instead of the index, return arr[clst]
}
Here is a fiddle where you can test it: http://jsfiddle.net/Lpzndcbm/4/
Unlike all the solution proposed here this solution runs in O(log(n)) and not in O(n). If you are not familiar with complexity, it means that this algorithm will find the closest value in an array of size N in at most O(log(N)) loop while the others will find it in at most N loop (with N = 10000, it makes a big difference since log(10000) ~ 14 (binary log)).
Note that if you have really small array, this may be slower than the naive algorithm.
There you go :
function closest(list, x) {
var min,
chosen = 0;
for (var i in list) {
min = Math.abs(list[chosen] - x);
if (Math.abs(list[i] - x) < min) {
chosen = i;
}
}
return chosen;
}
Each time, the minimum distance is computed and the chosen value is updated based on the minimum. (http://jsbin.com/dehifefuca/edit?js,console)
Something like this?
var closest = btnDrag.pos.reduce(function (prev, curr) {
return (Math.abs(curr - new_x) < Math.abs(prev - new_x) ? curr : prev);
});
Simple for loop will do it:
var btnDrag = {};
btnDrag['pos'] = [0, 65, 131, 196, 259, 323, 388, 453, 517];
new_x = 425;
var index = -1;
for (var i = 0; i < btnDrag.pos.length; i++)
{
if (i < btnDrag.pos.length-1) //loop till i is at 2 positions from the end.
{
//value has to be less then the selected value + 1
if (new_x < btnDrag.pos[i+1])
{
//calculate the half between the values and add it with the first value
// test if new_x is larger then that value.
if ((btnDrag.pos[i+1] - btnDrag.pos[i])/2 + btnDrag.pos[i] > new_x)
{
index = i;
break;
}
else
{
index = i+1;
break;
}
}
}
else
{
//edge cases.
if (new_x < 0)
{
index = 0;
}
else
{
index = btnDrag.pos.length-1;
}
}
}
document.body.innerHTML = btnDrag['pos'][index] + " (" + index + ")";
I would like to compare 2 lists and get 3 variables: moving item, offset, direction.
Example 1:
Old list = 121,120,119,43,42,41,40,39,38,37
New list = 43,121,120,119,42,41,40,39,38,37
I move item 43 up to the front of list,
moving item = 43
offset = 3 (between old and new position have 3 items)
direction = +1 (as it move up)
Example 2
Old nids= 121,120,119,43,42,41,40,39,38,37
New nids= 121,120,119,43,41,40,39,38,37,42
I move item 42 down at the end of list,
moving item = 42
offset = 5 (between old and new position have 5 items)
direction = -1 (as it move down)
I am using javascript to handle it. Which algorithm I can use? Or what is the hints for me to search in google ? Thank you very much!
Here's something tested only with your examples. Perhaps it can at least provide one possible direction. (I may have left out some boundary/special cases.)
var a1 = [121,120,119,43,42,41,40,39,38,37],
b1 = [43,121,120,119,42,41,40,39,38,37],
a2 = [121,120,119,43,42,41,40,39,38,37],
b2 = [121,120,119,43,41,40,39,38,37,42];
function f(a,b){
var item,
i = offset = 0,
direction;
while (a[i] == b[i]){
i++;
}
if (a[i + 1] == b[i]){
direction = -1;
item = a[i];
while (b[i] != item){
offset++;
i++;
}
} else {
direction = 1;
item = b[i];
while (a[i] != item){
offset++;
i++;
}
}
return [item,offset,direction]
}
Output:
console.log(f(a1,b1))
Array [ 43, 3, 1 ]
console.log(f(a2,b2))
Array [ 42, 5, -1 ]
I am working on looping through a few grid items and was hoping someone could help me find a way to figure out how to get to the items that I need and possibly I will better understand how to access the items in the grid. So here is the grid.
[0] [1] [2] [3] [4]
[5] [6] [7] [8] [9]
[10] [11] [12] [13] [14]
[15] [16] [17] [18] [19]
[20] [21] [22] [23] [24]
This is basically a 5x5 grid, however it could be any size but I just used this for the example. There are two ways I would like to loop through this. The first one being in this order:
0,1,2,3,4,9,14,19,24,23,22,21,20,15,10,5,6,7,8,13,18,17,16,11,12
Basically all that is doing is going around the outside starting from the top left. The next way I would want to loop through that is through the same exact values except in reverse order (basically inside out instead of outside in) and actually thinking about this now I could just loop through the first method backwards. If anyone can help me with this it would be great. I really want to learn more on how to loop through items in crazy arrangements like this.
This function
function n(i, w, h)
{
if (i < w)
return i;
if (i < w + h-1)
return w-1 + (i-w+1)*w;
if (i < w + h-1 + w-1)
return w-1 + (h-1)*w - (i - (w + h-1 - 1));
if (i < w + h-1 + w-1 + h-2)
return (h - (i - (w + w-1 + h-1 - 2)))*w;
var r = n(i - (w-1)*2 - (h-1)*2, w-2, h-2);
var x = r % (w-2);
var y = Math.floor(r / (w-2));
return (y+1)*w + (x+1);
}
accepts as input
i: Index of the item you're looking for
w: Width of the grid
h: Height of the grid
and returns the corresponding element of the grid assuming that clock-wise spiral traversal.
The implementation simply checks if we're on the top side (i<w), on the downward right side (i<w+h-1) and so on and for these cases it computes the cell element explicitly.
If we complete one single trip around the spiral then it calls recursively itself to find the element in the inner (w-2)*(h-2) grid and then extracts and adjusts the two coordinates considering the original grid size.
This is much faster for big grids than just iterating and emulating the spiral walk, for example computing n(123121, 1234, 3012) is immediate while the complete grid has 3712808 elements and a walk of 123121 steps would require quite a long time.
You just need a way to represent the traversal pattern.
Given an NxM matrix (e.g. 5x5), the pattern is
GENERAL 5x5
------- -------
N right 5
M-1 down 4
N-1 left 4
M-2 up 3
N-2 right 3
M-3 down 2
N-3 left 2
M-4 up 1
N-4 right 1
This says move 5 to the right, 4 down, 4 left, 3 up, 3 right, 2 down, 2 left, 1 up, 1 right. The step size shifts after each two iterations.
So, you can track the current "step-size" and the current direction while decrementing N, M until you reach the end.
IMPORTANT: make sure to write down the pattern for a non-square matrix to see if the same pattern applies.
Here's the "walking" method. Less efficient, but it works.
var arr = new Array();
for(var n=0; n<25; n++) arr.push(n);
var coords = new Array();
var x = 0;
var y = 0;
for(var i=0; i<arr.length; i++) {
if( x > 4 ) {
x = 0;
y++;
}
coords[i] = {'x': x, 'y': y};
x++;
}
// okay, coords contain the coordinates of each item in arr
// need to move along the perimeter until a collision, then turn.
// start at 0,0 and move east.
var dir = 0; // 0=east, 1=south, 2=west, 3=north.
var curPos = {'x': 0, 'y': 0};
var resultList = new Array();
for(var x=0; x<arr.length; x++) {
// record the current position in results
var resultIndex = indexOfCoords(curPos, coords);
if(resultIndex > -1) {
resultList[x] = arr[resultIndex];
}
else {
resultList[x] = null;
}
// move the cursor to a valid position
var tempCurPos = movePos(curPos, dir);
var outOfBounds = isOutOfBounds(tempCurPos, coords);
var itemAtTempPos = arr[indexOfCoords(tempCurPos, coords)];
var posInList = resultList.indexOf( itemAtTempPos );
if(outOfBounds || posInList > -1) {
dir++;
if(dir > 3) dir=0;
curPos = movePos(curPos, dir);
}
else {
curPos = tempCurPos;
}
}
/* int indexOfCoords
*
* Searches coordList for a match to myCoords. If none is found, returns -1;
*/
function indexOfCoords(myCoords, coordsList) {
for(var i=0; i<coordsList.length; i++) {
if(myCoords.x == coordsList[i].x && myCoords.y == coordsList[i].y) return i;
}
return -1;
}
/* obj movePos
*
* Alters currentPosition by incrementing it 1 in the direction provided.
* Valid directions are 0=east, 1=south, 2=west, 3=north
* Returns the resulting coords as an object with x, y.
*/
function movePos(currentPosition, direction) {
var newPosition = {'x':currentPosition.x, 'y':currentPosition.y};
if(direction == 0) {
newPosition.x++;
}
else if(direction == 1) {
newPosition.y++;
}
else if(direction == 2) {
newPosition.x--;
}
else if(direction == 3) {
newPosition.y--;
}
return newPosition;
}
/* bool isOutOfBounds
*
* Compares the x and y coords of a given position to the min/max coords in coordList.
* Returns true if the provided position is outside the boundaries of coordsList.
*
* NOTE: This is just one, lazy way of doing this. There are others.
*/
function isOutOfBounds(position, coordsList) {
// get min/max
var minx=0, miny=0, maxx=0, maxy=0;
for(var i=0; i<coordsList.length; i++) {
if(coordsList[i].x > maxx) maxx = coordsList[i].x;
else if(coordsList[i].x < minx) minx = coordsList[i].x;
if(coordsList[i].y > maxy) maxy = coordsList[i].y;
else if(coordsList[i].y < miny) miny = coordsList[i].y;
}
if(position.x < minx || position.x > maxx || position.y < miny || position.y > maxy) return true;
else return false;
}
This will move through the grid in a direction until it hits the bounds or an item already in the results array, then turn clockwise. It's pretty rudimentary, but I think it would do the job. You could reverse it pretty simply.
Here's a working example: http://www.imakewidgets.com/test/walkmatrix.html