Strategy for Looping Through a Fixed Set in Javascript by Steps - javascript

I am trying to figure out a way in JavaScript to advance and rewind through a fixed set by steps.
[0,1,2,3,4,5,6,7,8,9,10]
I'm starting at 3. I want to advance by 15. That means I land on 7 because after 10, we start over again at 0.
Another example. Starting at 9, rewind by 11. I should end up on 9 again because after 0 we start over again at 10.
Basically, as you loop you fall of the edges of the set and start again on the opposite edge continuing in the same direction.
This should work for any size set, not just one with 11 elements like in the example.
Help me, math wizards! 🧙‍♂️

Generally for any language what you want is the modulus operator %. The problem the behavior for negative numbers is not defined as some would expect. One way to deal with this:
function move(start, step, size) {
return (start+step+Math.abs(step)*size)%size
}
console.log(move(9,-11,11))
console.log(move(3,15,11))
where step can be either positive or negative. Another I think is:
function move(start, step, size) {
res = (start+step)%size;
if (res < 0) return size + res;
return res;
}
console.log(move(9,-11,11))
console.log(move(3,15,11))

You could get the index, add the wanted steps and take from the sum the remainder with the length of the array as index.
If the index is negative add the length of the array.
function getValue(array, start, steps) {
var index = array.indexOf(start);
if (index === -1) return;
index += steps;
index %= array.length;
return array[index < 0 ? index + array.length : index];
}
var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(getValue(array, 3, 15)); // 7
console.log(getValue(array, 9, -11)); // 9

Related

Create a function, validateCred() that has a parameter of an array. Scope of variables

I'm learning Javascript on Codecademy and have gotten stumped by a problem. I believe my issue is with the scope of my iterator tracker but not too sure.
Here are the directions given to me:
"Create a function, validateCred() that has a parameter of an array. The purpose of validateCred() is to return true when an array contains digits of a valid credit card number and false when it is invalid. This function should NOT mutate the values of the original array.
To find out if a credit card number is valid or not, use the Luhn algorithm. Generally speaking, an algorithm is a series of steps that solve a problem — the Luhn algorithm is a series of mathematical calculations used to validate certain identification numbers, e.g. credit card numbers. The calculations in the Luhn algorithm can be broken down as the following steps:
Starting from the farthest digit to the right, AKA the check digit, iterate to the left.
As you iterate to the left, every other digit is doubled (the check digit is not doubled). If the number is greater than 9 after doubling, subtract 9 from its value.
Sum up all the digits in the credit card number.
If the sum modulo 10 is 0 (if the sum divided by 10 has a remainder of 0) then the number is valid, otherwise, it’s invalid.
Here’s a visual that outlines the steps. Check your function using both the provided valid and invalid numbers."
Here's an array that should be passed into this function and return true:
const valid1 = [4, 5, 3, 9, 6, 7, 7, 9, 0, 8, 0, 1, 6, 8, 0, 8];
Here's an array that should return false:
const invalid1 = [4, 5, 3, 2, 7, 7, 8, 7, 7, 1, 0, 9, 1, 7, 9, 5];
Lastly, here's my code for trying to solve this:
function validateCred(arr) {
//Keeps track for the sum modulo later on
let totalSum = 0;
//Iterates to the left
for (let i = arr.length; i > 0; i--) {
//Keeps track of place to later double every other digit
let iterater = 1;
//Checks for every other digit and doubles it
if (iterater % 2 === 0) {
//Doubles it
let doubledAmnt = arr[i] * 2;
//Checks if doubled amount is greater than 9
if (doubledAmnt > 9) {
totalSum += (doubledAmnt - 9);
//Adds the doubled amount if not over 9
} else {
totalSum += doubledAmnt;
}
//Adds the digit if it does not need to be doubled
} else {
totalSum += arr[i];
};
//Adds to place tracker (I think my problem is here?)
iterater++
};
//Checks if the total sum is divisible by 10 to return true or false
if (totalSum % 10 === 0) {
return true;
} else {
return false;
}
}
function validateCred(arr) {
//Keeps track for the sum modulo later on
let totalSum = 0;
let iterater = 1; //iterater should be outside loop so that it wont reset in for loop
//Iterates to the left
for (let i = arr.length-1; i >= 0; i--) { //index starts in 0, so you should minus 1 to read the 16 digits
//Checks for every other digit and doubles it
if (iterater % 2 === 0) {
//Doubles it
let doubledAmnt = arr[i] * 2;
//Checks if doubled amount is greater than 9
if (doubledAmnt > 9) {
totalSum += (doubledAmnt - 9);
//Adds the doubled amount if not over 9
} else {
totalSum += doubledAmnt;
}
//Adds the digit if it does not need to be doubled
} else {
totalSum += arr[i];
};
//Adds to place tracker (I think my problem is here?)
iterater++
};
//Checks if the total sum is divisible by 10 to return true or false
if (totalSum % 10 === 0) {
return true;
} else {
return false;
}
}
Please note that indexes always start in 0 so if you start with 1, you have to minus 1 so that it will read the whole array. Next is, you put the iterater inside the loop, so it resets whenever the loop iterates, so you have to put it outside the loop. Hope this helps.
Please see the code I modified above with comments.

Breaking numbers down into components

I have two arrays and for each number in the first array I need to find the largest number from the second array that fits into that number and break the first number into its components. Since 25 from the second array is the largest number that fits into 80 of the first, I need to transform 80 into two numbers - 75, 5. Likewise for 6 and 5 the result should be 5, 1. So the end result should be an array [75, 5, 5, 1]
let arr = [80, 6]
let arr2 = [25, 5]
for (let x of arr) {
for (let y of arr2) {
if (x / y > 1 && x / y < 4) {
let mlt = Math.floor(x / y)
largestFit = y * mlt
arr.splice(arr.indexOf(x), 1, largestFit)
}
}
}
console.log(arr)
The code above gives [75, 5] so thought I could add one more splice operation to insert the remainders, but doing this arr.splice(arr.indexOf(x + 1), 0, x - largestFit) just crashes the code editor. Why isn't this working and what is the solution? Thank you.
It is not advised to splice an array that is being iterated, and it is the reason why your loop got suck sometimes.
Instead build a new array, so it doesn't affect the iteration on the input array.
If you first sort the second array in descending order, you can then find the first value in that array that fits the amount, and be sure it is the greatest one. For sorting numerically in descending order, you can use sort with a callback function.
Once you have found the value to subtract, you can use the remainder operator (%) to determine what is left over after this subtraction.
function breakDown(amounts, coins) {
// Get a sorted copy (descending) of denominations:
coins = [...coins].sort((a, b) => b - a);
const result = []; // The results are stored here
for (let amount of amounts) {
for (let coin of coins) {
if (coin <= amount) {
result.push(amount - amount % coin);
amount %= coin;
}
}
if (amount) result.push(amount); // remainder
}
return result;
}
// Example 1
console.log(breakDown([80, 6], [25, 5]));
// Example 2
console.log(breakDown([100, 90, 6], [100, 75, 15, 5, 1]));
Explanations
The coins are sorted in descending order so that when we search for a fitting coin from left to right, we can be sure that if we find one, it will be the greatest one that fits, not just any. So for instance, if our amount is 7 and we have coins 2 and 5, we don't want to use coin 2 just yet -- we want to use coin 5 first. So if we sort those coins into [5, 2], and then start looking for the first coin that is smaller than our amount, we will be sure to find 5 first. The result would not be as expected if we would have found 2 first.
We can calculate the remainder of a division with the % operator, and there is a shortcut for when we want to assign that remainder back to the amount: it is the %= operator. amount %= coin is short for amount = amount % coin.
When the inner loop completes, it might be that amount is not zero, i.e. there is still an amount that remains that cannot be consumed by any available coin. In that case we want to still have that remainder in the result array, so we push it.
Often, the amount will already be zero when the loop ends. This will be ensured when the smallest coin is the smallest unit of amount one can expect. For instance if the amount is expressed as an integer, and the smallest coin is 1, then there will never be any remainder left when the inner loop has completed. If however the smallest coin would be 2, and we are left with an a amount of 1, then there is no way to reduce that amount to zero, so after the loop ends, we could be stuck with that remainder. And so we have this code to deal with that:
if (amount) result.push(amount)
Floating point
Be careful with using non-integers: floating point arithmetic does not always lead to expected results, because numbers like 0.1 cannot be accurately represented in floating point. You can end up with a non-zero amount after the inner loop finishes, when zero would have been expected. That amount will be tiny like 1e-15, and really indicates there was such an inaccuracy at play.
When calculating with monetary amounts, it is common practice to do that in number of cents, so to make all calculations based on integers. This will give reliable results.
I found the issue. After the first splice() operation indexOf(x) was returning -1, since x's are being replaced, so the solution is to assign indexOf(x) to a variable and use that variable for both splice() operations.
let arr = [80, 6]
let arr2 = [25, 5]
for (let x of arr) {
for (let y of arr2) {
if (x / y > 1 && x / y < 4) {
let mlt = Math.floor(x / y)
largestFit = y * mlt
let idx = arr.indexOf(x)
arr.splice(idx, 1, largestFit)
arr.splice(idx + 1, 0, x - largestFit)
}
}
}
console.log(arr)

Sort a single dimensional array by the indexes listed in second single demential array - JavaScript

Here is the situation: I need the ability to reorder any single dimensional array so that the new array starts with the center number(if object count is odd) or the center 2 numbers (if object count is even) and iterates low, then high until all numbers in original array are accounted for.
Example 1 - odd number of objects:
Original Array: [1,2,3,5,8,13,20]
New Array: [5,3,8,2,13,1,20]
Example 2 - even number of objects:
Original Array: [1,2,3,4]
New Array: [2,3,1,4]
I have tried this with a for loop and can get it to work hypothetically, but I am not able to use a for loop as a computed property in Vue.js.
Here is my attempt which does not work:
gameInfo: {
cards: [1, 2, 3, 6, 8, 13, 21, 40, 1000],
}
reorderOddCards() {
ATTEMPT 1
const cardCount = this.gameInfo.cards.length;
const middleNumber = (cardCount / 2).toFixed(0);
const newCardOrder = this.gameInfo.cards.map(addToArray);
function addToArray(value, index) {
if (index < middleNumber) {
const newIndex = (((middleNumber - index) * 2) - 1);
newCardOrder.splice(newIndex, 1, value);
} else if (index === middleNumber) {
newCardOrder.splice(index, 1, value);
} else {
const newIndex = ((middleNumber - index) * 2);
newCardOrder.splice(newIndex, 1, value);
}
}
return newCardOrder;
},
Here is a seemingly better approach with a .sort function, but I can't seem to get it working either.
Potential Solution
This can be achieved with a simple while loop. The key here is finding the middle index(es). In an odd length array, there is only one center, which we can think of as having the left and right centers on the same point to generalize the solution. This index will be the result of flooring the length divided by two. The right index will always be this value as well. However, for even length arrays, we need to decrement the left index by one. After computing these indexes, we loop while decrementing the left index and incrementing the right index to add values to our result array.
function order(arr){
let right = Math.floor(arr.length / 2);
let left = right - (arr.length % 2 == 1 ? 0: 1);
let res = left === right ? [arr[left]] : arr.slice(left, right + 1);
while(left > 0){
res.push(arr[--left]);
res.push(arr[++right]);
}
return res;
}
console.log(...order([1,2,3,5,8,13,20]));
console.log(...order([1,2,3,4]));

Looking for a better way to reduce array and determine if numbers are in order

const dice = [1,3,4,5,6]
const score = straightScore(dice,4)
function straightScore(dice, sizeOfStraight) {
let score = 0
sizeOfStraight > 4 ? score = 40 : score = 30
dice.sort( (a, b) => a-b )
// filter to eliminate duplicates
const filteredDice = dice.reduce( (acc, die) => {
if ( acc.indexOf(die) === -1 ) acc.push(die)
return acc
}, [] )
//determine straight
const straightFinder = filteredDice.reduce( (acc, die) => {
const lastNumInArray = acc.slice(-1)[0]
// here is my hack if the small straight starts with 3 and the last die is a 1
if ( die - lastNumInArray === 2) acc = [die]
if ( lastNumInArray === undefined || die - lastNumInArray === 1 ) acc.push(die)
return acc
}, [] )
if (straightFinder.length >= sizeOfStraight) return score
return 0
}
console.log(score)
I got this to work, but it feels hacky. Any thoughts would be appreciated.
I am trying to make a basic Yahtzee game. Determining if a straight was rolled is where I got stuck. It basically worked, but if it was a small straight (4 in a row out of five dice) going from 3 - 6 AND the fifth die was a 1 my reduce would not work right. It always resolved to a 1 item array. I can see the problem in my logic, so I threw a little hack in there to make it work, but I feel like there must be a dozen better ways. Here is my code. Thanks for looking.
The dice are an array of 5 numbers 1-6 representing dice, the sizeOfStraight is just so I can reuse it for the large straight. The problem only came up with the small straight though, so you could just put a 4 in for that.
input dice = [1, 3, 4, 5, 6]
output (none, just returns because straightFinder.length = 1)
You don't really need reduce in that final operation. Just check the sorted dice at index 0 with index 3. If the latter has a value of 3 more than the first, the first 4 sorted dice represent a small straight. The same can be repeated at index 1 and index 4. There are no other positions possible for a small straight. A large straight can only start at index 0, so that only needs one check.
NB: extracting unique values can be done with the help of a temporary Set.
const dice = [1, 3, 4, 5, 6];
const score = straightScore(dice, 4);
function straightScore(dice, sizeOfStraight) {
let score = 0;
sizeOfStraight > 4 ? score = 40 : score = 30;
dice.sort( (a, b) => a-b );
// duplicates can be eliminated with a Set:
const filteredDice = [...new Set(dice)];
// determine straight: only two locations are of interest (at most)
if (filteredDice[sizeOfStraight-1] - filteredDice[0] === sizeOfStraight-1) return score;
if (filteredDice[sizeOfStraight] - filteredDice[1] === sizeOfStraight-1) return score;
return 0;
}
console.log(score);
Maybe this answer is a little overkill for Your needs, but may inspire You. Generally, I would create a "histogram" of rolled values and then look for the longest sequence of consecutive "hits" to find the longest straight.
Approach 1:
Let's say, we rolled dice = [6, 3, 1, 4, 3]. The "histogram" would look like this:
0 - false (there's no dice roll 0)
1 - true
2 - false
3 - true
4 - true
5 - false
6 - true
7 - false (there's no dice roll 7)
Indexes 0 and 7 are useful as boundaries, which are guaranteed to be always false (which simpifies detecting straights starting at 1 or ending at 6). It can be created like this:
Array(8).fill(false).map((_, i) => dice.indexOf(i) >= 0)
-> [false, true, false, true, true, false, true, false]
Now we need to find length of the longest sequence of trues. Let's traverse through the histogram and detect changes from false to true (straight start) and back (straight end):
function findStraights(dice) {
const histogram = Array(8).fill(false).map((_, i) => dice.indexOf(i) >= 0)
let straights = [];
let start = 0;
for (let i = 1; i < 8; i++) {
if (histogram[i - 1] === histogram[i])
continue;
if (histogram[i])
start = i
else
straights.push({ start: start, length: i - start })
}
return straights
}
console.log(findStraights([]))
console.log(findStraights([5]))
console.log(findStraights([3, 2, 1]))
console.log(findStraights([4, 5, 6, 1]))
console.log(findStraights([6, 3, 1, 4, 3]))
Now we have array of all found straights and finding the longest one is trivial (sort(...)[0]).
Approach 2:
Let's represent the histogram as a binary number (instead of the array shown above) and use a little bit-twiddling magic to determine longest sequence length:
function findLongestStraight(dice) {
let bits = dice.reduce((a, v) => a | (1 << v), 0)
let longest = 0
while (bits !== 0) {
bits &= bits << 1
longest++
}
return longest
}
console.log(findLongestStraight([]))
console.log(findLongestStraight([5]))
console.log(findLongestStraight([3, 2, 1]))
console.log(findLongestStraight([4, 5, 6, 1]))
console.log(findLongestStraight([6, 3, 1, 4, 3]))
The first line creates binary number bits, where every bit represents if given number was rolled on at least one die (bit 0 is always 0, bit 1 represents rolled 1 etc.) In our case bits = 0b01011010 = 90.
The while part uses a bit-twiddling trick, that shortens every sequence of consecutive 1s by one on every iteration. All we do is count number of iterations needed to zero all sequeces.

Pick 4 values in an array with a random starter index

My array looke like :
var arr = [0,1,2,3,4,5];
So I random starter point (index) like :
var start = getRandomInt(0,5);
function getRandomInt (min,max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
For example :
if start is 0, so return 0,1,2,3
if start is 1, so return 1,2,3,4
if start is 2, so return 2,3,4,5
if start is 3, so return 3,4,5,0
if start is 4, so return 4,5,0,1
if start is 5, so return 5,0,1,2
Just random a start index so loop that array
I wonder any easy way to do this ?
Playground : http://jsfiddle.net/2tSdb/
The main issue here is to move from the last index back to 0, which you can do with the modulo operation. You should do something similar to the following:
var data = [0,1,2,3,4,5];
var NUM_VALUES = 4; // Amount of values to take
var start = 3; // Replace with getRandomInt function
var values = [];
for(var i = start, j = 0; j < NUM_VALUES; j++, i = (i+1) % data.length) {
values.push(data[i]);
}
console.log(values); // [ 3, 4, 5, 0 ]
Variable i contains the current index of the Array we are going to access. It is initialized to start like you want. The variable j is only there to make sure we grab the number of values you want, 4 in this case. As you can see, j is incremented by 1 after each loop and we keep going until it is equal to NUM_VALUES. Nothing fancy there.
Variable i is also incremented by 1 (i = i+1), but we apply the modulo operation after that in order to keep the resulting number between 0 and data.length-1. Modulo simply results in the remainder after integer division, so 6 % 4 would be 2, 6 % 6 would be 0, etc. This is done so the index can never be higher than the last index. When i hits 6 here, the modulo would push it back to 0 like we want.

Categories