I have an array of sorted numbers, and a starting value. For the sake of simplicity, let's say the array has values from 1 to 20, and the starting value is 10.
The value to find can change every 5 seconds, based on user input. It can either increase or decrease and it always keeps in the range of values of the table.
The only thing I cannot know is whether the value is increasing or decreasing. I have come up with the (very) simple algorithm below.
Can you think of a way to improve it ?
Ideally, I think the best approach would be to run the two for loops simultaneously and return when value is found... Using workers for example ?
In my example, the "going down" for loops is exhausted before "going up" starts running. Which, idealy, shouldn't happen, since I'm trying to spread tries -1/+1 each time.
Btw : the reason why I'm trying to do this is because I have to run quite heavy functions in the for loops. The code is running in node.js context.
Here's a JSFiddle and the code below
const attempts = document.getElementById('attempts');
let attemptsCount = Number(attempts.textContent);
const lastValue = 10;
const valueToFind = 16; //this must be found in the least possible number of attempts
const table = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20];
const lookup = () => {
for (var i = lastValue; i > 0; i--) {
if (table[i] == valueToFind) {
alert('Found going down');
return;
} else {
attemptsCount++;
attempts.textContent = attemptsCount;
}
}
for (var i = lastValue; i < table[table.length-1]; i++) {
if (table[i] == valueToFind) {
alert('Found going up');
return;
} else {
attemptsCount++;
attempts.textContent = attemptsCount;
}
}
}
//find new value
lookup();
Right now, each for loop you have runs async from one another...so one side will always finish before the other side starts...which is not ideal.
Remember that for loops are set up to initialize, check if bool statement = true, and set next step...so like if statements, you can implement multiple statements in each scenario.
Reducing the loop attempts can be simple as doing it at the same time:
const lookup = () => {
for (var up = lastValue, down = lastValue-1; up < table[table.length-1] || down > 0; up++, down--) {
if (up < table[table.length-1] && table[up] == valueToFind) {
alert('Found going up');
return;
} else if (down > 0 && table[down] == valueToFind) {
alert('Found going down');
return;
} else {
attemptsCount++;
attempts.textContent = attemptsCount;
}
}
Your JSFiddle updated
Related
I am trying to iterate a count of every 10 in a while loop. The code below counts 200 10 times and then stops, the reason it stops is that I need to store the first 10 in a spreadsheet, but that is another problem to solve, right now, I am not sure how to go about moving onto the next 10 counts taken from the first 10 count in the while loop below.
while (go) {
data = getRecordsByPage(i,200,token,module);
if (Number(data.info.count) < 200) {
go = false;
};
if (i == 10) {
go = false;
while(go = false)
{
Utilities.sleep(10000)
}
if(Utilities.sleep == 10000)
{
go = true;
}
}
rows = Number(rows) + Number(data.info.count);
i++;
Logger.log(rows)
}
Also, please let me know if having a nested while loop in this is loop a good way to restart the loop again with the timer before it restarts.
How about using modulo?
replace if (i == 10) { by if (i%10 == 0) {
This way, it will enter in your if statement every multiple of 10
I've developed a small memory game, the game contains a function to flip cards for every turn. I'm looping thru the array containing the images every time to pick one card. I'm sure there's away to optimize this function so I don't have to loop thru the array every time in order to just turn one card. But I'm a bit clueless how to proceed and would like to get some guidance of how I possibly could optimize this function.
Can I create some sort of if statement perhaps? Note. Looking for vanilla javascript suggestions only and without creating an object to replace the array.
I'm clueless of another method then to loop thru the array.
function flipCards() {
var i; // Loopvar
// For loop
for (i = 0; i < cardsElems.length; i++) {
if (this == cardsElems[i]) {
if (cardsCounter > 1) {
return;
}
cardsElems[i].className = "brickFront";
cardsElems[i].src = "pics/" + allCards[i] + ".png";
removeListener(cardsElems[i], "click", flipCards);
if (cardsCounter < 1) {
card1 = i;
} else {
card2 = i;
}
cardsCounter += 1;
}
}
if (cardsCounter < 2) {
nextBtn.disabled = true; // Disable button
}
if (cardsCounter == 2) {
turns += 1;
turnNr.innerHTML = turns;
nextBtn.disabled = false; // Enable button
}
if (brickBack.length == 0) {
endGame();
}
} // End flipCards
how about using Loadash ?
It has plenty of function to manipulate arrays and very efficient.
you can store your data inside an object.By key(card name) value(fliped or not). then access by the key to change the value to be flipped or not.
example : {Ace: true} = {'cardName': 'isFlipped'}
I'm pretty confident that I have the basic logic behind my Simon game. However, I'm having issues in going to the next round. I have attempted to use an if statement and while loop but neither have worked. How should I proceed?
A brief explanation: gameColors is an array that has all the random colors that will be played out in an increasing number over several rounds. The variable sequencelength is used to increase the counter used in the user input function, whereas the variable sequenceLengthInv is the opposite of that, subtracted from gameColors.length to get sequenceLength.
random sequence displays the random sequence of colors up to the sequenceLength value.
JS:
if (sequenceLengthInv >= 0) {
// have the game play the random sequence of colors
for (var i = 0; i < gameColors.length - sequenceLengthInv; i++) {
randomSequence();
}
//listens for the user's input
for (var i = 0; i < color.length; i++) {
userInput();
}
sequenceLength++;
sequenceLengthInv--;
}
Try using for loops and using throw/reject & catch (or break). Also, Promises may be useful here.
async function playGame(availableColors, minColors, maxColors) {
await wait(333);
var colors = Array.from({ length: maxColors }, () => randomChoice(availableColors));
try {
for (var i = minColors; i <= maxColors; i++) {
console.log(colors.slice(0, i));
await playRound(colors.slice(0, i));
}
alert('You win!');
} catch (e) {
if(e !== undefined) {
throw e;
}
alert('You lose!');
}
}
My modified (and now commented) code is available here: https://repl.it/#solly_ucko/InconsequentialRoastedMonitor.
I am building a decorator for arrays of items, the array of objects is meant to be slotted into a defined range of values if it fits there.
Currently, I am doing this using some conditionals to check for the range but the code does not feel clean enough to me.
Does anyone have any suggestions about how write this code in a more concise and expandable way?
Example of current setup...
thingsToSort.forEach(function(thing) {
if (thing > 1 || thing < 3) {
// set the item to 1
}
if (thing > 3 || thing < 5) {
// set to 3
}
})
Note: I am really looking for a better way to loop through this logic and determine if object falls in the range.
One another implementation.
Created a function to represent the Range, Range
A function to identify the range and take appropriate action. setcompareRange
Notice the usage of the some method in the function compareRange. Since a number can be found in one range only, All the ranges are not evaluated and till the matched range traversal is done.
function Range(min, max){
this.min = min;
this.max = max;
}
var rangeArray = [ new Range(1,3), new Range(3,5)];
function compareRange(c,i,arr){
var result = rangeArray.some(x=> {
return setcompareRange(c, x.min, x.max)
});
}
function setcompareRange(thing, min, max){
if (thing > min && thing < max) {
// set the item to 1
console.log("set thing = " + thing + " in range = " + min);
return true;
}
}
var thingsToSort = [2,4];
thingsToSort.forEach(compareRange);
I would first double-check your logic...
thingsToSort.forEach(function(thing) {
This conditional will set ANYTHING greater than 1 to 1, and ignore the second condition (thing < 3):
if (thing > 1 || thing < 3) {
// set the item to 1
}
You should be using an && operator to AND these two conditions:
if (thing > 1 && thing < 3) {
// set the item to 1
}
The same thing goes for this conditional which will set ANYTHING greater than 3 to 3.
if (thing > 3 || thing < 5) { //should be &&
// set to 3
}
})
You are also not breaking the loop after meeting a conditional. This means that even though you have already determined that a thing meets the first condition, you are still checking to see if it meets the other conditions. This wastes resources. Use else if to prevent this:
if (thing > 1 && thing < 3) {
// set the item to 1
}
else if (thing > 3 && thing < 5) {
// set to 3
}
Other than that, it's already pretty clean. This is very similar to the classic fizzbuzz problem, of which, there are many possible refactorings
I am having multiple issues with my <select> element in angular and am trying to understand what is going on. My first step is to understand why the multiple console.log() messages I have put in for debugging repeatedly appear in the console, as in, instead of the message appearing once like I would expect, they appear an infinite number of times, as if part of an infinite loop. Is this how a function called from an ng-options is supposed to behave? If so, I don't understand why, if not, then I would like to fix my loop.
My html: <select ng-options="theorderdate as theorderdate for theorderdate in getOtherOrderDates(Bread.text)" ng-model="randomDateRomantic.randomDateRomantic" ng-change="soRomantic(Bread.text)"></select>
The console.log() messages appear from the getOtherOrderDates() function, which is below (with my comments included):
$scope.getOtherOrderDates = function(loaf) {
var istheLoafdaily = false;
var theorderdates = [];
for (var i = 0; i < $scope.theBreadsList.length; i++) {
for (var z = 0; z < $scope.theBreadsList[i].breads.length; z++) {
if ($scope.theBreadsList[i].breads[z].text == loaf && i > 0) //not a daily loaf, goes beyond "Daily Breads"
{
console.log(theorderdates);
theorderdates = theorderdates.concat($scope.theBreadsList[i].breads[z].orderDates); //concat the matched bread's order dates
console.log(theorderdates, $scope.theBreadsList[i].breads[z].orderDates);
theorderdates = _.sortBy(theorderdates, function(m) {
return m.getTime()
});
for (var y = 0; y < theorderdates.length; y++) {
theorderdates[y] = theorderdates[y].toLocaleDateString();
}
theorderdates = _.uniq(theorderdates);
if (theorderdates.length > 0) {
console.log("Something is wrong here"); //problem
$scope.randomDateRomantic.randomDateRomantic = theorderdates[0];
}
console.log(theorderdates);
return theorderdates;
} else if ($scope.theBreadsList[i].breads[z].text == loaf && i == 0) { //a daily loaf, i == 0
console.log("The bread matched is daily", loaf); //***
istheLoafdaily = true;
console.log(theorderdates); //***
theorderdates = theorderdates.concat($scope.theBreadsList[i].breads[z].orderDates); // concat the matched bread's order dates
console.log(theorderdates, $scope.theBreadsList[i].breads[z].orderDates); //***
break; // escape the for loop, should it be two breaks?????? yes...
} else if (istheLoafdaily && i > 0 && $scope.theBreadsList[i].breads[z].orderDates.length > 0) { //not sure what scenario this matches, hence wtf
theorderdates = theorderdates.concat($scope.theBreadsList[i].breads[z].orderDates);
console.log("wtf");
}
}
}
//end of outermost for loop
//not sure what this is doing because this functionality is repeated up there^ (for non-daily breads)
theorderdates = _.sortBy(theorderdates, function(m) {
return m.getTime()
});
for (var y = 0; y < theorderdates.length; y++) {
theorderdates[y] = theorderdates[y].toLocaleDateString();
}
theorderdates = _.uniq(theorderdates);
if (theorderdates.length > 0) {
$scope.randomDateRomantic.randomDateRomantic = theorderdates[0];
console.log("Something is wrong here (daily)"); //problem
}
return theorderdates;
//not sure what this is doing because this functionality is repeated up there^ (for non-daily breads)
//if change to Locale date string then not unique, but if don't change then not a date to sort!!!!!!! >:(
},
I am getting almost all console messages an infinite number of times, without doing anything such as firing the ng-change function. I just add a daily bread to my cart for instance, and then the console gets filled with the following messages, that I have starred in my code.
My theBreadsList is not very long, so there is something going on that it is going repeatedly like this. Even if I broke out of the for loop twice as you will see in my code, it wouldn't explain the fact that it logs to the console all the time, because eventually the loop would not be satisfied, and this wouldn't take to long as has been mentioned.
Please advise, thank you. If you need more information, I am happy to provide.
The getOtherOrderDates will be called in each digest cycle so that angular knows whether to update options in select. That's most likely the reason you're seeing this method being called many times.
If you're worried about performance impact of this loop you can build the options upfront inside your controller store it in $scope like so:
$scope.options = $scope.getOtherOrderDates($scope.Bread.text);
whenever $scope.Bread.text changes and then use $scope.options inside your template.
To avoid triggering your loops in every digest loop you can use one time binding ::value.
<select ng-options="theorderdate as theorderdate for theorderdate in ::getOtherOrderDates(Bread.text)"
ng-model="randomDateRomantic.randomDateRomantic"
ng-change="soRomantic(Bread.text)"></select>
Thanks to that expression inside ng-options will be evaluated only once and the watcher will be removed after first evaluation which will stop your function being triggered in next digest loop iterations.
DOCS