Trouble understanding solution for New Years Chaos on Hackerrank (Javascript) - javascript

The problem can be found here
I struggled with this for quite some time - and I've always neglected learning CS related material in Javascript or in general. But I will be interviewing at some different companies and some have notified that the tech interview will be data structure/algorithm related, so I'm currently going through some courses online and completing challenges on Hackerrank.
Anyway - I eventually caved after a few hours and looked up some solutions.
One being this:
let queue1 = [2, 1, 5, 3, 4];
let queue2 = [2, 5, 1, 3, 4];
function minimumBribes(q) {
let bribes = 0;
let currentIndex = q.length - 1;
let positionDiff;
while (currentIndex >= 0) {
const currentValue = q[currentIndex];
const valuePosition = currentIndex + 1;
positionDiff = currentValue - valuePosition;
if (positionDiff >= 3) {
return "Too chaotic";
}
const briberPosition = currentValue - 2;
for (let comparePosition = Math.max(0, briberPosition); comparePosition < currentIndex; comparePosition++) {
const compare = q[comparePosition];
if (compare > currentValue) {
bribes++;
}
}
currentIndex--;
}
return bribes;
}
minimumBribes(queue1)
I understand that we need to check if a number has moved forward more than 2 spaces, and checking its value vs position does this.
However, getting to
const briberPosition = currentValue - 2;
for (let comparePosition = Math.max(0, briberPosition); comparePosition < currentIndex; comparePosition++) { ... }
is leaving me confused. I've ran through this slowly in debugger with different values and I can't wrap my head around why we set the currentValue - 2 to the briberPosition, and then checking that vs the index.
Could someone share some light?

The final question of the challenge is "HOW MANY bribes are needed to get the queue into this state".
So he chose to go from the end on every number and first checks if its 3 or more places ahead of its original position- this part you already got.
Now, if its not 3 or more places away- you want to know HOW many places away, this can be done simply by going 2 places back, and check if the numbers 2 places away and 1 place away are greater than the value in our current position. If they are- it means they bribed him to get there so we increment the "bribes" variable and go on to the next integer and repeat the process. Since he goes at the array from the end, he decrements the position to get to the farthest place a "briber" of our current value can be.
If it helps, this whole for loop can be completely skipped when positionDiff is 0, if he checked that after the too chaotic check, it could be a little more efficient.
Notice that in the "for" loop he trims it to be the Math.max(0, briberPosition) to avoid going beyond array limit.

Related

LeetCode two sum not working as expected with a hash graph

I am trying to come up with an efficient solution to the two sum function on leetCode. From what I understand this code SHOULD work, but it keeps on returning undefined
var twoSum = function(nums, target) {
let hashGraph = {}
let slow = 0
let fast = nums.length - 1
while(slow < Math.abs(nums.length / 2)) {
if(nums[slow] + nums[fast] == target) {
return [slow, fast]
} else if(!hashGraph[target - nums[slow]] === undefined) {
let answer = [slow, hashGraph[target - nums[slow]]].sort()
return answer
} else if(!hashGraph[target - nums[fast]] === undefined) {
return [hashGraph[target - nums[fast]], fast].sort()
} else {
hashGraph[nums[slow]] = slow
hashGraph[nums[fast]] = fast
slow++
fast--
}
}
};
essentially I am storing the values at each index inside of a hash graph and assigning the values at that location to the index that the number was found. When I iterate through I am checking if the complement for the number at the current index exists in the hash table. If it does I return the current index and the value of the found index (which is the value in the array that the number was discovered on)
For the first test case I am given the array [2,7,11,15] and a target of 9
what SHOULD happen here is that the while loop's else case is hit and the graph is populated as follows:
{
2: 0,
15: 3
}
Then on the second iteration the second condition is hit where it checks if
hashGraph[target - nums[slow]] is valid. Given the target of 9 and the input of 7 I am essentially asking it if hashGraph[9-2] or hashGraph[2] exists. Indeed it does, however when visualizing the execution with python tutor's Javascript execution visualizer it fails this check and reaches the else clause.
This is what is stumping me. hashGraph[2] does exist. I can replicate the same thing and get the correct result if I use the following:
let hash = {
2: 0,
15: 3
}
let arr = [7]
console.log(hash[9 - arr[0]])
If that code gets me the correct result then why does my if condition fail?

Why is my array appearing empty after appearing to have an element in it?

I'm trying to make a discord bot that randomly shuffles an array of planets and then picks one to hide a bounty on. Then, if a player has that planet role, they can search for the bounty, The problem is whenever I search for the bounty on the planet, it says that the array is empty, but when I shuffle the planets and show the result, the array doesn't appear to be empty.
This is the shuffling code.
let coreworlds = [verzan, albregao, corellia, rishi, coruscant, vurdon, wobani]
let coreworldsresult = []
case 'start':
function shuffle(coreworlds) {
for (let i = coreworlds.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
[coreworlds[i], coreworlds[j]] = [coreworlds[j], coreworlds[i]];
}
}for (let i = 0; i < 1000000; i++) {
shuffle(coreworlds);}
coreworldsresult.push(coreworlds[1]);
message.channel.send('A small bounty has been set somewhere in the core worlds. You will have 6 hours to find the bounty.');
message.channel.send(coreworldsresult);// This always shows the random world.
setTimeout(() => {
if(coreworldsresult.lenght != 0){
coreworldsresult.length = 0
message.channel.send('Nobody claimed the bounty in time. It`ll take me another 12 hours before I can find another small bounty.')
}else{
coreworldsresult.length = 0
message.channel.send('I`m gonna be getting another small bounty in 12 hours.')
}
}, 21600000);
setInterval(() => {
function shuffle(coreworlds) {
for (let i = coreworlds.length - 1; i > 0; i--) {
let j = Math.floor(Math.random() * (i + 1));
[coreworlds[i], coreworlds[j]] = [coreworlds[j], coreworlds[i]];
}
}for (let i = 0; i < 1000000; i++) {
shuffle(coreworlds);}
coreworldsresult.push(coreworlds[1])
message.channel.send('A small bounty has been set somewhere in the core worlds. You will have 6 hours to find the bounty.')
setTimeout(() => {
if(coreworldsresult.lenght != 0){
coreworldsresult.length = 0
message.channel.send('Nobody claimed the bounty in time. It`ll take me another 12 hours before I can find another small bounty.')
}else{
coreworldsresult.length = 0
message.channel.send('I`m gonna be getting another small bounty in 12 hours.')
}
}, 21600000);
}, 64800000);
This is the searching code.
case 'search':
message.channel.send('You look around for the bounty.')
message.channel.send(coreworldsresult.length);// This always comes back as "0"
if(member.roles.cache.some(r => r.name === coreworldsresult || midrimresult || outerrimresult)){
message.reply('you found a bounty! Now shoot him with `!attack <large, medium, or small>-
bounty <weapon>`!')
}else{
message.reply('you did not find anything. Try a different planet.')
}
break;
I figured out that the reason was because my arrays were declared under the
client.on('message', message =>{
so the changes to them only applied to the code in the same case area. I fixed this by putting the arrays at the top.
Being a new to programming, one thing I would suggest when getting stuck with unexpected outcomes is to work from the top down and create cases where you KNOW what the outcome should be and eventually you'll get down to the part of your code that has either a syntax error or a logical error.
I usually add console logs to all my conditionals when debugging problems.
Like #EvanMorrison said you have typo in the word length
setTimeout(() => {
if(coreworldsresult.lenght != 0){
also please use the DRY principal (Dont't repeat yourself), by wrapping your code in functions and then use them as many time as you need

Function recursion Maximum call stack size exceeded

I am writing the Sudoku game and having trouble finding the reason for the stack overflow. In simple words, I am filling a block with numbers 1-9 randomly. But before actually putting the number in the block, I also need to check whether there is such a number in the column it belongs to and if there is, re-random the number and check again. I've been trying a function like
let columns = [];
for(i = 0; i < 9; i++){
let a = [];
columns.push(a);
}
randomCell.textContent = unusedNumbers[getRandomNumber(0, (unusedNumbers.length - 1))];
function checkColumn() {
if(columns[columnIndex - 1].includes(randomCell.textContent)) {
randomCell.textContent = unusedNumbers[getRandomNumber(0, (unusedNumbers.length - 1))];
checkColumn();
} else {
columns[columnIndex - 1].push(randomCell.textContent);
unusedNumbers.splice(unusedNumbers.indexOf(randomCell.textContent), 1);
}
}
checkColumn();
It should work just fine, and 2 of 3 times it does and I get a generated Sudoku with non-repeating numbers in both blocks and columns. But there are cases in which it loops endlessly somehow and reaches the maximum call stack size. I have also tried the same thing with the
while (1 == 1) {
if(columns[columnIndex - 1].includes(randomCell.textContent) == false) {
columns[columnIndex - 1].push(randomCell.textContent);
unusedNumbers.splice(unusedNumbers.indexOf(randomCell.textContent), 1);
break;
}
randomCell.textContent = unusedNumbers[getRandomNumber(0, (unusedNumbers.length - 1))];
}
instead of a function, but it's the same - 2 times it generates as it should, and the third time the page just freezes in an endless loop. I feel there is a small and obvious mistake in the condition which I do not see.
Edit: I get the numbers not from an RNG but from an array
let unusedNumbers = ['1','2','3','4','5','6','7','8','9'];, and after I actually pick a number after both checks, I am splicing it from the array. The RNG works with indexes here.

Algorithm to Sort a List of n Number of Items by Comparing them Against One Another in Pairs

I will try to be specific as possible as I can't find anything on this through the Google gods.
I have a list of 10 movies. I would like to display the movies in pairs. The user picks their favorite of the two. The next pair is displayed. The user picks their favorite of those two, so on and so on until I can faithfully output the list in their order of preference from 1-10.
I'm doing this in Javascript, but even just a way to do it that is language agnostic would be great. No need to worry about syntax or UI stuff.
With three movies it's pretty easy (initial order of movies and order of pairs shouldn't matter):
1.sw
2.esb
3.rotj
example 1
1vs2: winner = 2
2vs3: winner = 2
1vs3: winner = 1
Sorted List: 2,1,3
example 2
1vs3: winner = 1
1vs2: winner = 2
2vs3: winner = 2
Sorted List: 2,1,3
First time posting so if I need to be more specific, need to have exact syntax, etc., please don't hesitate to let me know.
The minimum number of comparisons required to sort 10 items is 22. (See https://oeis.org/A036604). Do you really think your users will suffer through 22 "which movie do you like better?" questions? And do you honestly believe that the result will be useful? You'll have many cases where a user will say that he liked movie A better than B, and B better than C, but he liked movie C better than he liked movie A. So now you have the problem that:
A > B
B > C
C > A
And there's no reasonable way to resolve that conflict.
In short, your user interface is flawed. You can try to build it, but your users won't like it and your results will not be reliable.
A better interface would be to list the 10 movies and allow the users to arrange them in order of preference. Or let the user rate the movies on a scale from 1 to 5. But expecting users to answer 22 questions and get a complete ordering is a fool's errand.
The basic problem is easy. We have a ton of sorting algorithms that will work with O(n log(n)) comparisons. For instance mergesort:
// Split the array into halves and merge them recursively
function mergeSort (arr) {
if (arr.length === 1) {
// return once we hit an array with a single item
return arr
}
const middle = Math.floor(arr.length / 2) // get the middle item of the array rounded down
const left = arr.slice(0, middle) // items on the left side
const right = arr.slice(middle) // items on the right side
return merge(
mergeSort(left),
mergeSort(right)
)
}
// compare the arrays item by item and return the concatenated result
function merge (left, right) {
let result = []
let indexLeft = 0
let indexRight = 0
while (indexLeft < left.length && indexRight < right.length) {
if (left[indexLeft] < right[indexRight]) {
result.push(left[indexLeft])
indexLeft++
} else {
result.push(right[indexRight])
indexRight++
}
}
return result.concat(left.slice(indexLeft)).concat(right.slice(indexRight))
}
So in principle you need to simply replace left[indexLeft] < right[indexRight] with an arbitrary comparison function that asks the user, gets an answer, and then continues.
Now there is a catch. The catch is that you need to make this code asynchronous. When you go to ask the user, you need to ask the user, then return to inside your code. If you're using node at the console, then you can do this with async/await. If you are not, then you'll need to figure out how to do it with promises. Modifying mergeSort is easy, just make the end into:
return Promise.all([mergeSort(left), mergeSort(right)]
).then(function (values) {return merge(values[0], values[1])});
The trick is in turning the loop inside of merge into a function that takes the current state of your iteration, and returns a promise that asks the question, then either returns the final sorted array, or returns a promise that handles the next iteration.
Since this looks like homework, whose whole purpose is to make you face that mental challenge, I'll leave my answer off here. Fair warning, I gave you a hint about how to do it, but if you're just learning about async code your head is SUPPOSED to spin as you figure it out.
To determine all possible combinations from your array try the following loop. Assuming order does not matter and we do not want repeats:
var arr = [1,2,3,4,5,6,7,8,9,10]
var arr_count = arr.length
var combinations_array = []
for (i = 0; i < arr_count; i++) {
var combinations = arr_count - (i+1)
for (y = 0; y < combinations; y++) {
combination = [arr[i],arr[(combinations - y)]];
combinations_array.push(combination);
}
}
In your example I'd pass Movie ID's into arr then iterate through the combinations_array to determine which combination of movies should be displayed next.
To produce a list of pairs, I would use a nested loop like this:
var items = [1,2,3,4,5,6,7,8,9,10],
result = [],
x = 0,
y = 0;
for (x = items.length; x--;)
{
for(y = x; y--;)
{
result.push({ a: items[x], b: items[y] });
}
}
console.debug(result);
The second loop is initialised from the outer loops incrementor so that you don't end up with duplicates.
Once you have the pairs, you should be able to build the ui from that.
Hope it helps!

alternatives for excessive for() looping in javascript

Situation
I'm currently writing a javascript widget that displays a random quote into a html element. the quotes are stored in a javascript array as well as how many times they've been displayed into the html element. A quote to be displayed cannot be the same quote as was previously displayed. Furthermore the chance for a quote to be selected is based on it's previous occurences in the html element. ( less occurrences should result in a higher chance compared to the other quotes to be selected for display.
Current solution
I've currently made it work ( with my severely lacking javascript knowledge ) by using a lot of looping through various arrays. while this currently works ( !! ) I find this solution rather expensive for what I want to achieve.
What I'm looking for
Alternative methods of removing an array element from an array, currently looping through the entire array to find the element I want removed and copy all other elements into a new array
Alternative method of calculating and selecting a element from an array based on it's occurence
Anything else you notice I should / could do different while still enforcing the stated business rules under Situation
The Code
var quoteElement = $("div#Quotes > q"),
quotes = [[" AAAAAAAAAAAA ", 1],
[" BBBBBBBBBBBB ", 1],
[" CCCCCCCCCCCC ", 1],
[" DDDDDDDDDDDD ", 1]],
fadeTimer = 600,
displayNewQuote = function () {
var currentQuote = quoteElement.text();
var eligibleQuotes = new Array();
var exclusionFound = false;
for (var i = 0; i < quotes.length; i++) {
var iteratedQuote = quotes[i];
if (exclusionFound === false) {
if (currentQuote == iteratedQuote[0].toString())
exclusionFound = true;
else
eligibleQuotes.push(iteratedQuote);
} else
eligibleQuotes.push(iteratedQuote);
}
eligibleQuotes.sort( function (current, next) {
return current[1] - next[1];
} );
var calculatePoint = eligibleQuotes[0][1];
var occurenceRelation = new Array();
var relationSum = 0;
for (var i = 0; i < eligibleQuotes.length; i++) {
if (i == 0)
occurenceRelation[i] = 1 / ((calculatePoint / calculatePoint) + (calculatePoint / eligibleQuotes[i+1][1]));
else
occurenceRelation[i] = occurenceRelation[0] * (calculatePoint / eligibleQuotes[i][1]);
relationSum = relationSum + (occurenceRelation[i] * 100);
}
var generatedNumber = Math.floor(relationSum * Math.random());
var newQuote;
for (var i = 0; i < occurenceRelation.length; i++) {
if (occurenceRelation[i] <= generatedNumber) {
newQuote = eligibleQuotes[i][0].toString();
i = occurenceRelation.length;
}
}
for (var i = 0; i < quotes.length; i++) {
var iteratedQuote = quotes[i][0].toString();
if (iteratedQuote == newQuote) {
quotes[i][1]++;
i = quotes.length;
}
}
quoteElement.stop(true, true)
.fadeOut(fadeTimer);
setTimeout( function () {
quoteElement.html(newQuote)
.fadeIn(fadeTimer);
}, fadeTimer);
}
if (quotes.length > 1)
setInterval(displayNewQuote, 10000);
Alternatives considered
Always chose the array element with the lowest occurence.
Decided against this as this would / could possibly reveal a too obvious pattern in the animation
combine several for loops to reduce the workload
Decided against this as this would make the code to esoteric, I'd probably wouldn't understand the code anymore next week
jsFiddle reference
http://jsfiddle.net/P5rk3/
Update
Rewrote my function with the techniques mentioned, while I fear that these techniques still loop through the entire array to find it's requirements, at least my code looks cleaner : )
References used after reading the answers here:
http://www.tutorialspoint.com/javascript/array_map.htm
http://www.tutorialspoint.com/javascript/array_filter.htm
http://api.jquery.com/jQuery.each/
I suggest array functions that are mostly supported (and easily added if not):
[].splice(index, howManyToDelete); // you can alternatively add extra parameters to slot into the place of deletion
[].indexOf(elementToSearchFor);
[].filter(function(){});
Other useful functions include forEach and map.
I agree that combining all the work into one giant loop is ugly (and not always possible), and you gain little by doing it, so readability is definitely the winner. Although you shouldn't need too many loops with these array functions.
The answer that you want:
Create an integer array that stores the number of uses of every quote. Also, a global variable Tot with the total number of quotes already used (i.e., the sum of that integer array). Find also Mean, as Tot / number of quotes.
Chose a random number between 0 and Tot - 1.
For each quote, add Mean * 2 - the number of uses(*1). When you get that that value has exceeded the random number generated, select that quote.
In case that quote is the one currently displayed, either select the next or the previous quote or just repeat the process.
The real answer:
Use a random quote, at the very maximum repeat if the quote is duplicated. The data usages are going to be lost when the user reloads/leaves the page. And, no matter how cleverly have you chosen them, most users do not care.
(*1) Check for limits, i.e. that the first or last quota will be eligible with this formula.
Alternative methods of removing an array element from an array
With ES5's Array.filter() method:
Array.prototype.without = function(v) {
return this.filter(function(x) {
return v !== x;
});
};
given an array a, a.without(v) will return a copy of a without the element v in it.
less occurrences should result in a higher chance compared to the other quotes to be selected for display
You shouldn't mess with chance - as my mathematician other-half says, "chance doesn't have a memory".
What you're suggesting is akin to the idea that numbers in the lottery that haven't come up yet must be "overdue" and therefore more likely to appear. It simply isn't true.
You can write functions that explicitly define what you're trying to do with the loop.
Your first loop is a filter.
Your second loop is a map + some side effect.
I don't know about the other loops, they're weird :P
A filter is something like:
function filter(array, condition) {
var i = 0, new_array = [];
for (; i < array.length; i += 1) {
if (condition(array[i], i)) {
new_array.push(array[i]);
}
}
return new_array;
}
var numbers = [1,2,3,4,5,6,7,8,9];
var even_numbers = filter(numbers, function (number, index) {
return number % 2 === 0;
});
alert(even_numbers); // [2,4,6,8]
You can't avoid the loop, but you can add more semantics to the code by making a function that explains what you're doing.
If, for some reason, you are not comfortable with splice or filter methods, there is a nice (outdated, but still working) method by John Resig: http://ejohn.org/blog/javascript-array-remove/

Categories