angular, skipping over a value if it has a certain key/value - javascript

Bit of a weird problem here - so I will try to explain this as clearly as possible.
I have a simple ng-repeat that will show content based on what has a key value of .active set to true. I let the user scroll through the content with some arrow buttons bound to some ng-clicks. This works great, however I want to exclude one item from the array if it has the key value of side = 'help' attached to it. So basically I want the arrow clicks to skip over it in a sense. I have no control unfortunately where in the array the help item is. So here are the click functions
//flip right
$scope.flipRight = function(index, parent){
var idx = index + 1;
if (idx >= $scope.contentHere[parent].sides.length) {
idx = 0;
}
$scope.contentHere[parent].sides[index].active = false;
$scope.contentHere[parent].sides[idx].active = true;
};
//flip left
$scope.flipLeft = function(index, parent){
var idx = index - 1;
if (idx < 0) {
idx = $scope.contentHere[parent].sides.length - 1;
}
$scope.contentHere[parent].sides[index].active = false;
$scope.contentHere[parent].sides[idx].active = true;
};
So basically what I am trying to figuire out is how to have this logic skip over the item if it has .side = 'help'. I thought about using lodash to _filter the array by items that do not have the value, but it will offset the index so that will not work. I am not sure how to approach this (maybe I am thinking about this incorrectly?), and could use some direction.
Thank you for taking the time to read!

$scope.flipRight = function(index, parent){
var idx = index + 1;
if(idx >= $scope.contentHere[parent].sides.length){
idx = 0;
}
if($scope.contentHere[parent].sides[idx] == 'help'){
$scope.flipRight(idx, parent); //Added to skip over to next item
$scope.contentHere[parent].sides[index].active = false; // Added for the first item does not turn .active to false Issue
return; // Added to skip execution of following line of codes incase of recursion
}
$scope.contentHere[parent].sides[index].active = false;
$scope.contentHere[parent].sides[idx].active = true;
};
//flip left
$scope.flipLeft = function(index, parent){
var idx = index - 1;
if (idx < 0) {
idx = $scope.contentHere[parent].sides.length - 1;
}
if($scope.contentHere[parent].sides[idx] == 'help'){
$scope.flipLeft(idx, parent); //Added to skip over to next item
$scope.contentHere[parent].sides[index].active = false; // Added for the first item does not turn .active to false Issue
return; // Added to skip execution of following line of codes incase of recursion
}
$scope.contentHere[parent].sides[index].active = false;
$scope.contentHere[parent].sides[idx].active = true;
};

Related

Finding entry in 'different places' when navigating an implicit binary search tree

So I created an implicit binary tree, and a simple way of navigating through that tree that reacts when, for example, you push the left arrow, right arrow, or up arrow:
var items = [ .... ];
function newItem() {
var me = this;
this.item_location = 1;
this.leftItem = function() {
var left = items[(me.item_location * 2)];
if(left){
me.theItem = //create element, insert item from 'left' var, display item
me.item_location *= 2;
} else {}
}
this.rightItem = function() {
var right = items[(me.item_location * 2) + 1];
if(right){
me.theItem = //create element, insert item from 'right' var, display item
me.item_location = (me.item_location * 2) + 1;
} else {}
this.upItem = function() {
var up = items[Math.floor(me.item_location / 2)];
if(up){
me.theItem = //as above
me.item_location = Math.floor(me.item_location / 2);
} else {
// abandon setting this item, return to a different context/menu
}
}
My issue is that I'm finding 'duplicates' that aren't actually there, so there must be a bug in either this tree navigation code or in the code I'm using to check for duplicates:
function findItem(theItem) {
var itemLocations = [];
for(var i = items.length - 1; i >= 0; i--) {
if(items[i] === theItem) {
itemLocations.push(i);
}
}
for(var i = itemLocations.length - 1; i >= 0; i--) {
console.log(itemLocations[i]);
}
console.log(itemLocations);
return itemLocations;
}
What is wrong with this? It's consistently ending up with certain entries showing up twice, but the findItem function consistently finds only one entry.
There's nothing more to the code that could possibly cause this, so I feel like I've missed something super obvious.

Javascript: Detect and Remove a loop from cylic linked list

I have used Javascript to write a circular linked list and to detect and remove the loop.It is working fine untill the part of loop detection. How ever it is failing to remove the loopnode. More specifically: the removeLoop function of this code doesnot work.
Here is my code:
function Node(element){
this.element = element;
this.next = null;
}
//circular linked list class
function LList() {
this.head = new Node("head");
this.head.next = this.head;
this.find = find;
this.insert = insert;
this.display = display;
}
function find(item){
var curr = this.head;
while(curr.element != item){
curr = curr.next;
}
return curr;
}
//inserting items into linked list
function insert(newElem, after){
var newNode = new Node(newElem);
var curr = this.find(after);
newNode.next = curr.next;
curr.next = newNode;
}
function display() {
var currNode = this.head;
while ((currNode.next !== null) &&
(currNode.next.element !== "head")) {
console.log(currNode.next.element);
currNode = currNode.next;
}
}
function findPrevious(item){
var curr = this.head;
while(curr.next !== null && curr.next.element !== item){
curr =curr.next;
}
return curr;
}
//creating a linkedlist object
var furniture = new LList();
furniture.insert("chair","head");
furniture.insert("table", "chair");
furniture.insert("couch", "table");
furniture.insert("stool","couch");
//furniture.display();
//detecting if a linked list is circular
function detectALoop(list){
var slow = list.head;
var fast = list.head;
while(slow && fast && fast.next){
slow = slow.next;
fast = fast.next.next;
if(slow === fast){
removeLoop (slow, list);
return 1;
}
}
return 0;
}
//This part of the code doesnot work
function removeLoop(loopNode, list)
{
var ptr1 = loopNode;
var ptr2 = loopNode;
var looplen = 1,i;
// count the number of nodes in loop
while(ptr1.next != ptr2)
{
ptr1 = ptr1.next;
looplen++;
}
console.log(looplen)
ptr1 = list.head;
ptr2 = list.head;
for(i=0; i <= looplen; i++)
{
ptr2 = ptr2.next;
}
while(ptr2.next != ptr1.next)
{
ptr1 = ptr1.next;
ptr2 = ptr2.next;
}
ptr2.next = null; // breaking the loop
}
console.log(detectALoop(furniture))
furniture.display();
You are making this a lot more complicated than it needs to be if the loop has to be back onto the first element.
function breakLoop(list) {
var head = list.head, tail = head, len = 1;
while (tail.next != head) {
len++;
tail = tail.next;
}
tail.next = null;
console.log(len.toString());
}
Now if you may need to handle any arbitrary loop, I still have no idea what you need 3 loops for. Use an ES6 Set; most browsers now support this, I believe. I'm going to go ahead and return the length instead of logging it.
function breakLoopAnywhere(list) {
var seen = new Set, node = list.head;
while (!seen.has(node.next)) {
seen.add(node);
node = node.next;
}
node.next = null;
return seen.size;
}
If you don't have sets, you can hack it with an array, replacing has with indexOf and add with push.
If you feel you must have the ability to detect a loop vs a non-looping list without breaking it:
// takes a node, returns the node
// that points backwards on its next
function getLoopNode(node) {
var seen = new Set;
do {
seen.add(node);
} while (!seen.has(node.next) && node = node.next)
return node;
}
function detectLoop(node) {
return getLoopNode(node) != null;
}
function breakLoop(node) {
node = getLoopNode(node);
if (node) node.next = null;
}
Your detectALoop is less complicated, but it's wrong. The only loop this will detect is if node 2i loops back onto node i. But the list could be 3 elements long looping onto the start; it could be lots of numbers that aren't 2i and i. Since there are probably a lot of numbers, way too many to try them all, you can't fix this strategy. There is no clever way to find cycles in a graph that is any faster or more intuitive than the one I wrote above. As far as I know.
This variable is messed up...
var looplen = 1,i;
It looks like you want it to be a 1.
Your removeLoop code is wrong, it never terminates:
let's assume this list:
A -> B -> C -> A
with loop length 3.
You correctly find the loop length, 3, you then set ptr1 and ptr2 to the head of the list, and then call .next on ptr2 for the length of the loop + 1 times (because of <=).
// for i = 0; i <= 3
A.next -> B // i = 0
B.next -> C // i = 1
C.next -> A // i = 2
A.next -> B // i = 33
So in the end you have ptr2 = B and ptr1 = A, i.e. ptr2 === ptr1.next!
One is the next of the other, and in the while loop you advance both until one is equal to the other, but they will never be, because they always be one the next of the other!
If you change the <= to just < it works, but the second while loop is actually useless.

Recursion to return check both array[i+1] and array[i-1]

I'm making a timeline, and want to layer 'activities' based on how many overlaps occur.
I've found several answers on stack overflow on how to count overlapping intervals, though in my case I want the count to increase when an the overlap is indirect.
I've come up with the following recursive method:
countOverlaps: function(i, allItems) {
var currentItem = allItems[i];
// Declare position variables
var currentItemStart = this.getStartTimeMinutes(currentItem.timeStartString);
var currentItemEnd = currentItemStart + currentItem.duration;
var nextItemStart = (i < allItems.length - 1) ? this.getStartTimeMinutes(allItems[i + 1].timeStartString) : null;
var nextItemEnd = (nextItemStart != null) ? nextItemStart + allItems[i + 1].duration : null;
var prevItemStart = (i >= 1) ? this.getStartTimeMinutes(allItems[i - 1].timeStartString) : null;
var prevItemEnd = (prevItemStart != null) ? prevItemStart + allItems[i - 1].duration : null;
// The logic
// If the next item is an overlap, test the next item
// If the previous item is an overlap, test the previous item
if (currentItemEnd > nextItemStart && currentItemStart < nextItemEnd && nextItemStart != null) {
return 1 + this.countOverlaps((i + 1), allItems); // BUT how do I do the same for the previous one?
} else {
return 0;
}
},
But now I'm stuck. I think it's working how I want, except that it's only counting forward. If I want to check backwards and forward, will each recursive call not test the same index again and again?
Like all recursions you do something with only ONE element/ item in the function. Remember the terminiation - this is the most important thing in a recursion (no it is not the self call, because without it it won't be a recursion at all).
After that you will call yourself with another modified parameter.
Since I understand you correctly you want to start somewhere and go to left and right as far you want. Look at the terminiation code. You should change the condition to your needs.
The start sum left and sum right is not part of the recursion, because you only want to go in one direction per recursion.
This code is simple so you can easly adapt it to you need.
function sum(index, array){
function sumRecursion(index, array, direction){
// the termination ;)
if (index < 0) return 0;
if (index > array.length) return 0;
// do some stuff with your array at the current index.
// sorry, I did'nt read your logic code
var count = ...
// now the recursion
return count + sum(index + direction, array, direction);
}
return sumRecursion(index, array, -1) + sumRecursion(index, array, +1);
}

angular, swapping content from array, how to manage first and last

I am swapping content based on a loaded in array (so the length of the array will be dynamic each time) and I'm trying to figuire out how to handle the first and last item in this method. As in - I need when you click left from the first one it would go to the last and opposite for the last.
So I just have this -
<div class="side{{side.name}} side" ng-repeat="side in sides" ng-show="side.active" >
{{side.name}}
<i class="fa fa-chevron-circle-left" ng-click="flipLeft($index)"></i>
<i class="fa fa-chevron-circle-right" ng-click="flipRight($index)"></i>
</div>
and in the controller it just looks like this
$scope.flipLeft = function (index){
$scope.sides[index].active = false;
$scope.sides[index-1].active = true;
};
$scope.flipRight = function (index){
$scope.sides[index].active = false;
$scope.sides[index+1].active = true;
};
So all it really does is swap the .active to true or false to show or hide the content. I'm not sure how to switch around my logic so it loops around so that if you click left on the first item it goes to the last and visa versa. Thanks in advance!
Check if the index you want to set is greater than or less than the minimum/maximum values. If so, adjust:
$scope.flipLeft = function (index){
var idx = index - 1;
if (idx < 0) {
idx = $scope.sides.length - 1;
}
$scope.sides[index].active = false;
$scope.sides[idx].active = true;
};
$scope.flipRight = function (index){
var idx = index + 1;
if (idx >= $scope.sides.length) {
idx = 0;
}
$scope.sides[index].active = false;
$scope.sides[idx].active = true;
};
The easiest way is to use the mod method so that incrementing or decrementing stays in the range of valid numbers. So looking at your code I would recommend
$scope.flipLeft = function (index){
$scope.sides[index].active = false;
index = (index-1).mod($scope.sides.length)
$scope.sides[index].active = true;
};
And be sure to implement this trick: Javascript modulo not behaving.
$scope.flipLeft = function (index){
var sides = $scope.sides,
first = index,
second = (index === 0) ? sides.length - 1 : first - 1;
sides[first].active = false;
sides[second].active = true;
};
$scope.flipRight = function (index){
var sides = $scope.sides,
first = index,
second = (index === sides.length - 1) ? 0 : first + 1;
sides[first].active = false;
sides[second].active = true;
};

JQuery Click event crashes program with no console errors

EDIT**
In a game I am creating I use the next question button to move onto other questions in the grid if the user is having trouble with the current one. At the moment I have had real problems with it as it keeps on crashing my program, and not giving any console errors. The last problem I had with it was that it said "too much recursion". Since then I thought I had sorted the problem, but I have just done a few tests and it crashes every time.
This is the click event for the button...
//Next question click event
$('.next-question').bind("click", function() {
$('td').removeClass('highlight-problem');
shuffleEqually(listOfWords);
shuffleEqually(nextWordIndexes);
var rndWord = nextWordIndexes[Math.floor(Math.random())];
var rndWord = nextWordIndexes[2];
//Adds and removes nesesary classes
$('td[data-word="' + listOfWords[rndWord].name + '"]').addClass('highlight-problem');
$('td[data-word=' + word + ']').removeClass('wrong-letter').removeClass('wrong-word').removeClass('right-letter');
var spellSpace = $('td[data-word="' + listOfWords[rndWord].name + '"]').hasClass('right-word');
if (spellSpace) {
$('.next-question').trigger('click');
} else {
$("#hintSound").attr('src', listOfWords[rndWord].audio);
hintSound.play();
$("#hintPic").attr('src', listOfWords[rndWord].pic);
$('#hintPicTitle').attr('title', listOfWords[rndWord].hint);
}
});
I think it may have something to do with the if statement, but have tried changing it to this..
if (spellSpace == false) {
$("#hintSound").attr('src', listOfWords[rndWord].audio);
hintSound.play();
$("#hintPic").attr('src', listOfWords[rndWord].pic);
$('#hintPicTitle').attr('title', listOfWords[rndWord].hint);
}
and it makes it even worse
ShuffleEqually:
//Shuffles words to randomize
shuffleEqually(nextWordIndexes);
var shuffledWords = [];
shuffledWords = chosenWords.sort(function () {
return 0.5 - Math.random();
});
function shuffleEqually(a1, a2) {
var arrays = [];
if (typeof a1 === 'object' && a1.length > 0) {
arrays.push(a1);
}
if (typeof a2 === 'object' && a2.length > 0) {
arrays.push(a2);
}
var minLength = arrays[0].length;
jQuery.each(arrays, function (i, a) {
minLength = a.length < minLength ? a.length : minLength;
});
var randoms = [];
for (i = 0; i < minLength; i++) {
randoms.push(Math.random());
}
jQuery.each(arrays, function (i, a) {
var i = minLength;
while (i--) {
var p = parseInt(randoms[i] * minLength);
var t = a[i];
a[i] = a[p];
a[p] = t;
}
});
};
Hint sound:
var hintSound = $("#hintSound")[0];
Your issue is an infinite loop, plain and simple.
$('.next-question').bind("click", function() {
// binds click...
...
if (spellSpace) {
$('.next-question').trigger('click');
// triggers click ON THE SAME ELEMENT COLLECTION (same selector)
You want to refine this. I assume you want the trigger to work on the next question, so I suggest changing the second statement to:
$(".next-question").eq(($(".next-question").index($(this)) + 1) % $(".next-question").length).trigger("click");
You have a second infinite loop in shuffleEqually:
jQuery.each(arrays, function (i, a) {
var i = minLength;
while (i--) {
var p = parseInt(randoms[i] * minLength);
var t = a[i];
a[i] = a[p];
a[p] = t;
}
Change the while condition to have a limiting value, or it will loop endlessly (as a decrement operation always succeeds).

Categories