Determine direction of change in array - javascript

Given a sorted array, eg. [1,2,3,4,5] and a variation of this array, where one of the items got moved to a different position (so no longer sorted) eg. [1,2,5,3,4] or [2,3,4,1,5] I need to reliably determine the "direction" of the move - left or right depending on which way that one element got moved
(5 got moved left in first example, 1 got moved right in a second example)
At the moment I have a solution that walks (from the left) over arrays and compares their values at index i - as soon as they are not the same (to determine point of discrepancy), it then compares values at i+1. if old[i+1] > new[i+1] then left, otherwise right
//pseudocode:
function(old, now) {
for(var i in old) {
if (old[i] !== now[i]) {
return old[i+1] > now[i+1] ? "left" : "right"
}
}
}
EDIT
is there a more reliable (and potentially more efficient) solution to that problem? at the moment it gives "false positives" in certain situations. Do we event need the first array? its always sorted and sequential.

There is not really any room for improvements in your solution - You won't find a way to solve your problem without going trough all members of the array and checking them against the member in the other array.
Your current time complexity is O(n), which is actually not too bad. Therefore you should not worry about this function and if your application is working too slow, you should look for other fields to improve on.

Not really an answer, more discussion of the algorithm.
If you want guaranteed iteration from 0 and to ignore inherited enumerable properties, replace for..in with a for, while or do loop.
Given iteration from index 0, the algorigthm you have doesn't work. e.g. in the following:
var old = [1,2,3];
var now = [2,1,3];
value 1 has been moved right. The algorithm will find a miss-match at index 0, then compare the values at index 1. old[1] is greater than now[1] so "left" is returned.
It's also impossible to determine the direction of some moves, e.g. in the above was 1 moved right or 2 moved left? Another example:
var old = [1,2,3,4,5];
var new = [1,2,3,5,4];
Did 4 move right or 5 move left? A rule is required to determine which takes precidence.

Related

Reverse array looping - Going back to the last index when index 0 is surpassed [duplicate]

This question already has answers here:
JavaScript % (modulo) gives a negative result for negative numbers
(13 answers)
Closed 4 years ago.
I'm currently extending the Array.prototype to include a simple 'pointer' system, as I can move through there on several different spots without looping.
Array.prototype.current = 0;
This pointer can move both forwards and backwards through the array, using the following method to go forwards:
Array.prototype.next = function () {
return this[(this.current + 1) % this.length];
};
This line of code allows the pointer to be increased by one, unless it reaches the final index, in which case if it is increased again, it will return to index 0 instead of going out of bounds (because of the modulo).
However, now I want the exact same, but when decreasing the pointer instead. This would create something similar, i.e.:
Array.prototype.prev = function () {
return this[(this.current - 1) /* similar to modulo here */];
};
What I want here is that the pointer gets decreased by 1 under any normal circumstance. However, if the pointer was 0, it needs to go back to the last index of the array instead, so effectively reverse looping around. But, I want this with as minimal code as possible, like the modulo solution for increasing the pointer but looping it back to start if it reaches the end.
Is there a short-hand solution like the modulo solution possible for this?
EDIT
In response to the duplication flag. The referenced question and answers there indeed do apply to my question, so I understand the marking. However, it does not reference such a case of looping a list backwards and looping from front to back like this case (without branches, that is). After a considerable amount of time I hadn't run into this solution, so I'd think this question would result in positive impact for others running into a similar issue like mine.
you can try this
Array.prototype.prev = function () {
return this[(this.current - 1 + this.length) % this.length]
};

.forEach only moving half of array elements

Firstly, I'm new to JavaScript and in an effort to gain a better understanding of OOP I've decided to build solitaire (the three-card version mind you). In solitaire--as I'm sure is well known-- you deal out the cards in the whatever pattern and the remaining cards are placed off to the side; I'm calling these the 'reserve' deck. Then you move the cards you dealt around following the rules of the game (can only place a red 6 on a black 7, etc.) and when you run out of options you can pull three cards off the top of 'reserve' and see if you can use the third card to continue playing. If not, you pull three more cards, so on and so forth until you run out of cards in the 'reserve' deck. At this point, all the cards you pulled off the top of 'reserve' deck can then get placed back into the 'reserve' deck and then reused in the same manner.
And this is where my javascript is breaking. So I've created a Table object with the properties playSlots, winSlots, reserve and pick. playSlots is an array of arrays in which cards get dealt into, reserve is an array of the cards existent within the 'reserve' deck, winSlots isn't relative to this question and pick is an array that becomes populated with cards from reserve as it becomes necessary throughout game play. Pretty self-explanatory. When reserve becomes empty and you want to put the pick cards back into it, my code for this is currently:
if(!Array.isArray(newGame.reserve) || !newGame.reserve.length){
newGame.pick.forEach(function(cur, i, arr){
newGame.reserve.push(arr.shift());
});
}
The problem with this is that this only iterates through half of the cards in the pick array. I went through it in the debugger and it is, in fact adding the cards back into the reserve deck one at a time but once it gets to the 13th card it breaks out of the loop (28 cards cards get dealt out in the solitaire pattern, leaving 24 in the reserve deck...).
I was pretty sure this issue has something to do with the !newGame.reserve.length condition. That condition is something I found here:
Check if array is empty / does not exist. JS
and although the answer-er is pretty clear in his his explanation of this, I'm still kinda fuzzy on what it means. Thing is, I also tried this for a condition:
if(newGame.reserve[0] === undefined)
and had the exact same result. The .forEach loop is only iterating over half of the array elements.... What am I missing here?
You can achieve this easier using Array splice
Note: this reverses pick to the end of reserve, whereas your code does not
If you do not need it reversed, simply remove the .reverse()
It's not clear if you want pick reversed into reserve or not
var newGame = {
pick: [1,2,3,4],
reserve: [10,11,12]
};
newGame.reserve.push(...newGame.pick.splice(0, newGame.pick.length).reverse());
console.log(newGame);
in "old school" javascript, this can be written using .push.apply
var newGame = {
pick: [1,2,3,4],
reserve: [10,11,12]
};
newGame.reserve.push.apply(newGame.reserve, newGame.pick.splice(0, newGame.pick.length).reverse());
console.log(newGame);

How do I check my condition on an if statement to see if it is working correctly?

I'm trying to make a type of circular display which cycles through a series of values as well as moving text elements within an svg file. It uses the hammer.js library and uses the drag event. The values can go in either direction. I have it working to some degree. As the last value is shown from an array, it goes back to the beginning of the array to get the first values. Or vice-versa.
var keyArray = ["C","C#","Db","D","D#","Eb","E","F","F#","Gb","G","G#","Ab","A","A#","Bb","B"];
This is my array. Here is how I wrap it past the end of the array and back to the beginning.
** As per the request of a few commenters and the suggested solution by Nina, I have modified the code below to reflect their suggestions. I have also added a few more lines for clarity of what is happening overall.**
var delta = keyArray.length - 5; // can be constant, it is always positive
for(i=0;i<5;i++){
//5 svg text element containing 5 musical keys
keys = document.getElementById("keys"+i);
//ev.deltaX is the change received from the hammer pan event
//text element moves relative to its original starting X
keys.setAttribute("x",startingSpots[i]+ev.deltaX%150);
currentX=keys.getAttribute("x");
currentEdgeIndex=keyArray.indexOf(keys.textContent);
//This if is what appears to be failing.
if (keys.getAttribute("x")>=565){
keys.setAttribute("x",currentX-150);
keys.textContent = keyArray[(currentEdgeIndex + delta) % keyArray.length];
}
}
With the suggested changes, I removed the Number() calls as well as implementing the modulus for the wrapper. The behavior is still erratic. On the example below, if you pan to the right, as the first text element reaches 565, it meets the condition for the if, is moved back to the left by 150.
What it should do next is to change the textContent to the next appropriate value in the array. However, this is where it becomes erratic, it is no longer past 565 so it does not meet the condition of the if statement, but the text changes at every increment of the pan event as if it were.
I am sure I am not seeing something simple that is causing the trouble but not sure what it is.
The array does appear to be circling correctly, though I'm still not sure "How can I check to see if the if statement is being correctly evaluated and met?"
The project can be viewed here. http://codepen.io/cmgdesignstudios/pen/zrmQaE?editors=1010
* Edit with solution *
Nina suggested the problem lie in the handling of the touch event. After further investigation, I found she was correct. I had originally been moving the object relative to its starting position and the deltaX from the touch event. I then was trying to change the current position by simply moving it back to the left rather than adjusting the starting position.
So I replaced the
keys.setAttribute("x",currentX-150);
with
startingSpots[i]-=150;
This kept the movement relative to the starting position and the deltaX of the touch event.
Please delete the Number(...) casting where it's not necessary. ...length returns always number and the result of calculation is a number too.
Your actual key feature is to move 5 entries down, and this can be achieved wit a simple addition and a modulo, to keep the value in a specific range, like
keys.textContent = keyArray[(keyArray.length + currentEdgeIndex - 5) % keyArray.length];
Further simplified can this calculation then lead to just add a positive number of
delta = keyArray.length - 5; // can be constant, it is always positive
keys.textContent = keyArray[(currentEdgeIndex + delta) % keyArray.length];
and make the modulo out of it.

Arrays Inside Objects: Conditional Updating Of One Object's Array From Another Object's Array

DISCLAIMER
I have absolutely no idea how to succinctly describe the nature of the problem I am trying to solve without going deep into context. It took me forever to even think of an appropriate title. For this reason I've found it nearly impossible to find an answer both on here and the web at large that will assist me. It's possible my question can be distilled down into something simple which does already have an answer on here. If this is the case I apologise for the elaborate duplicate
TL;DR
I have two arrays: a main array members and a destination array neighbours (technically many destination arrays but this is the tl;dr). The main array is a property of my custom group object which is auto-populated with custom ball objects. The destination array is a property of my custom ball object. I need to scan each element inside of the members array and calculate distance between that element and every other element in the members group. If there exist other elements within a set distance of the current element then these other elements need to be copied into the current element's destination array. This detection needs to happen in realtime. When two elements become close enough to be neighbours they are added to their respective neighbours array. The moment they become too far apart to be considered neighbours they need to be removed from their respective neighbours array.
CONTEXT
My question is primarily regarding array iteration, comparison and manipulation but to understand my exact dilemma I need to provide some context. My contextual code snippets have been made as brief as possible. I am using the Phaser library for my project, but my question is not Phaser-dependent.
I have made my own object called Ball. The object code is:
Ball = function Ball(x, y, r, id) {
this.position = new Vector(x, y); //pseudocode Phaser replacement
this.size = r;
this.id = id;
this.PERCEPTION = 100;
this.neighbours = []; //the destination array this question is about
}
All of my Ball objects (so far) reside in a group. I have created a BallGroup object to place them in. The relevant BallGroup code is:
BallGroup = function BallGroup(n) { //create n amount of Balls
this.members = []; //the main array I need to iterate over
/*fill the array with n amount of balls upon group creation*/
for (i = 0; i < n; i++) {
/*code for x, y, r, id generation not included for brevity*/
this.members.push(new Ball(_x, _y, _r, _i)
}
}
I can create a group of 4 Ball objects with the following:
group = new BallGroup(4);
This works well and with the Phaser code I haven't included I can click/drag/move each Ball. I also have some Phaser.utils.debug.text(...) code which displays the distance between each Ball in an easy to read 4x4 table (with duplicates of course as distance Ball0->Ball3 is the same as distance Ball3->Ball0). For the text overlay I calculate the distance with a nested for loop:
for (a = 0; a < group.members.length; a++) {
for (b = 0; b < group.members.length; b++) {
distance = Math.floor(Math.sqrt(Math.pow(Math.abs(group.members[a].x - group.members[b].x), 2) + Math.pow(Math.abs(group.members[a].y - group.members[b].y), 2)));
//Phaser text code
}
}
Now to the core of my problem. Each Ball has a range of detection PERCEPTION = 100. I need to iterate over every group.members element and calculate the distance between that element (group.members[a]) and every other element within the group.members array (this calculation I can do). The problem I have is I cannot then copy those elements whose distance to group.members[a] is < PERCEPTION into the group.members[a].neighbours array.
The reason I have my main array (BallGroup.members) inside one object and my destination array inside a different object (Ball.neighbours) is because I need each Ball within a BallGroup to be aware of it's own neighbours without caring for what the neighbours are for every other Ball within the BallGroup. However, I believe that the fact these two arrays (main and destination) are within different objects is why I am having so much difficulty.
But there is a catch. This detection needs to happen in realtime and when two Balls are no longer within the PERCEPTION range they must then be removed from their respective neighbours array.
EXAMPLE
group.members[0] -> no neighbours
group.members[1] -> in range of [2] and [3]
group.members[2] -> in range of [1] only
group.members[3] -> in range of [1] only
//I would then expect group.members[1].neighbours to be an array with two entries,
//and both group.members[2].neighbours and group.members[3].neighbours to each
//have the one entry. group.members[0].neighbours would be empty
I drag group.members[2] and group.members[3] away to a corner by themselves
group.members[0] -> no neighbours
group.members[1] -> no neighbours
group.members[2] -> in range of [3] only
group.members[3] -> in range of [2] only
//I would then expect group.members[2].neighbours and group.members[3].neighbours
//to be arrays with one entry. group.members[1] would change to have zero entries
WHAT I'VE TRIED
I've tried enough things to confuse any person, which is why I'm coming here for help. I first tried complex nested for loops and if/else statements. This resulted in neighbours being infinitely added and started to become too complex for me to keep track of.
I looked into Array.forEach and Array.filter. I couldn't figure out if forEach could be used for what I needed and I got very excited learning about what filter does (return an array of elements that match a condition). When using Array.filter it either gives the Ball object zero neighbours or includes every other Ball as a neighbour regardless of distance (I can't figure out why it does what it does, but it definitely isn't what I need it to do). At the time of writing this question my current code for detecting neighbours is this:
BallGroup = function BallGroup(n) {
this.members = []; //the main array I need to iterate over
//other BallGroup code here
this.step = function step() { //this function will run once per frame
for (a = 0; a < this.members.length; a++) { //members[a] to be current element
for (b = 0; b < this.members.length; b++) { //members[b] to be all other elements
if (a != b) { //make sure the same element isn't being compared against itself
var distance = Math.sqrt(Math.pow(Math.abs(this.members[a].x - this.members[b].x), 2) + Math.pow(Math.abs(this.members[a].y - this.members[b].y), 2));
function getNeighbour(element, index, array) {
if (distance < element.PERCEPTION) {
return true;
}
}
this.members[a].neighbours = this.members.filter(getNeighbour);
}
}
}
}
}
I hope my problem makes sense and is explained well enough. I know exactly what I need to do in the context of my own project, but putting that into words for others to understand who have no idea about my project has been a challenge. I'm learning Javascript as I go and have been doing great so far, but this particular situation has me utterly lost. I'm in too deep, but I don't want to give up - I want to learn!
Many, many, many thanks for those who took the time read my very long post and tried provide some insight.
edit: changed a > to a <
I was learning more on object literals, I'm trying to learn JS to ween myself off of my jQuery dependency. I'm making a simple library and I made a function that adds properties of one object to another object. It's untested, but I think if you were apply something similar it might help. I'll try to find my resources. Btw, I don't have the articles on hand right now, but I recall that using new could incur complications, sorry I can't go any further than that, I'll post more info as I find it.
xObject could be the ball group
Obj2 could be the members
Obj1 could be the destination
/* augment(Obj1, Obj2) | Adds properties of Obj2 to Obj1. */
// xObject has augment() as a method called aug
var xObject = {
aug: augument
}
/* Immediately-Invoked Function Expression (IIFE) */
(function() {
var Obj1 = {},
Obj2 = {
bool: true,
num: 3,
str: "text"
}
xObject.aug(Obj1, Obj2);
}()); // invoke immediately
function augment(Obj1, Obj2) {
var prop;
for (prop in Obj2) {
if (Obj2.hasOwnProperty(prop) && !Obj1[prop]) {
Obj1[prop] = Obj2[prop];
}
}
}

Boolean Randomness - Recursion... or not?

Trying to create a random (0/1) boolean operation. I have a multidimensional array set up. So far, everything works. Am I on the right path? I currently use recursion; is there a more efficient way to do this?
function randomMove(){
var cs=b.length,
cr=Math.floor((Math.random()*cs)+1)-1,
cc=Math.floor((Math.random()*cs)+1)-1;
if(b[cr][cc]===""){ // THIS IS DEPENDENT ON EXISTING CODE. VIEW FIDDLE. //
// DO STUFF //
} else { randomMove(); }
}
Is this considered a good way to create a random move for 2 players? Or is recursion the wrong process here?
Fiddle
Select the cells that can possibly be selected, then choose a random one by index.
http://jsfiddle.net/Ehqka/1/
$.fn.random = function(){
return this.eq(Math.floor(Math.random()*this.length));
};
function randomMove(){
$("#board td").not(".o,.comp").random().addClass('comp');
testWin();
}
It selects all cells in the board, drops the ones that have a class of o or comp from the selection, generates a random number between 0 and the number of remaining cells, then selects the cell at that index to add a class to.
Note, however, your method will be "faster" than the above on the first move due to the fact that on the first move, every move is a valid one therefore it will never iterate past the first time. Mine can be slightly optimized to be more efficient on the first by removing the .not(), but your's should still be "faster" because it doesn't have to go to the DOM.
"faster" is relative, my method will grow less efficient as the table grows than yours will on the first move. As more and more moves happen, your method will become less and less efficient since more and more of the possible moves are invalid.

Categories

Resources