Losing the logic in my Javascript program - javascript

I have an issue with Javascript. I want to make the program , which generates 6 random numbers ( from 1 to 49 ).Anyway im doing it with for loop and it works. But when I try to find if any of those 6 numbers has a duplicate number , to replace it with a new random number. I've looked at js array duplicate functions , Im applying it , but it seems my fault is in the logic , still beginner with js , so please help me :)
js code:
var arr = new Array(7);
var numbers = [];
for(var i=1; i<arr.length;i++){
numbers[i] = Math.floor(Math.random()*48)+1;
if(numbers[i] == numbers[i+1]){
arr.indexOf(numbers[i]);
arr.push(Math.floor(Math.random()*48));
}
}
i`m trying to apply indexOf ( to find the duplicate number's index, and after that to replace it with new element via push()). Please give me some advises how to improve on my programming logic skills :(

First, JavaScript arrays are zero-indexed, so the first index is 0, not 1. Although it is valid to skip the first index and use the second to seventh indices instead, you will usually want to avoid that.
Second, why do you check numbers[i] against numbers[i + 1]? At that point, numbers[i + 1] won't have been set yet as it will be set in the next iteration. Additionally, even if this problem did not exist, your code would fail to catch this sequence: [ 5, 3, 5 ].
Third, I am not quite sure why you use two different arrays. Although you definitely can, there is no good reason to do so.
I will suggest another way to solve your problem and add comments to the steps necessary. I hope these will help you to understand this solution to your problem.
// Create an array for the chosen numbers
var numbers = [];
// Loop to generate 6 elements, zero-based
for(var i = 0; i < 6; i++) {
// This variable will hold the number we choose
var number;
// This is a do-while loop which generates a random integer
do {
// Generate a random integer between 1 and 49 (incl.)
number = Math.floor(Math.random() * 48) + 1;
} while(numbers.indexOf(number) != -1); // Exit loop once the number is not contained in the array
// Store the number in the array
numbers[i] = number;
}
This algorithm is short (8 SLOC) and pretty efficient.

This is one way you could do it:
var numbers = [];
for(i=0;i<6;i++){
numbers[i] = Math.floor(Math.random()*48)+1;
//this loop will help us check all of the already assigned numbers.
for(x=0;x<i;x++){
if(numbers[x] == numbers[i]){
i--;
}
}
}
This might not be the most efficient and optimal way to do it, but it sure works and it will give you no repetition on the numbers now that it will loop untill it finds a different number. Its a fast and short solution, yet not the most efficient. Hope this helps =).

for (var a=[],i=1;i<40;++i) a[i]=i;
function shuffle(array) {
var tmp, current, top = array.length;
if(top) while(--top) {
current = Math.floor(Math.random() * (top + 1));
tmp = array[current];
array[current] = array[top];
array[top] = tmp;
}
return array;
}
a = shuffle(a);
Random Numbers , No repeats

This statement will never be true:
if(numbers[i] == numbers[i+1]){
When you start, numbers array is empty. Then you assign one random to element 0. Then you try to compare element 0 in numbers to 1, but numbers[1] will be undefined.
The best way to do this is to start with an empty arr array, meaning length of 0. Loop until length equals 6. In the loop, generate a random number and only push the value into the arr array if the number doesn't exist in there.
UPDATE:
After seeing some of the answers, I think this is probably the simplest:
var arr = [],
num;
while (arr.length < 6) {
num = Math.floor(Math.random() * 48) + 1;
if (arr.indexOf(num) === -1) {
arr.push(num);
}
}
console.log(arr);

Related

Problem when learning forEach method in Javascript

I start learning Javascript since a week and I try to do some practice about the loop,
So I try to make a function which each time I call it, I doubled the numbers in an array.
I manage to do it with the classic "for" loop like this as an example:
var numbers = [2, 5, 37, 500, 75];
function getDoubled() {
for (var i = 0; i < numbers.length; i++) {
(numbers[i] = numbers[i] * 2);
}
}
So here the numbers in my array doubled each time I call the function getDoubled(); and now I try to do the same with forEach method.
var numbers = [2, 5, 37, 500, 75];
function getDoubled(array) {
array.forEach(function(number) {
(number = number * 2);
})
}
But I wasn't able to change the array when I call the function getDoubled(numbers), however if I test with console.log(number*2), it worked in the console, so I wasn't able to figure it out the mistake.
Reassigning an identifier has no side-effects. That is, doing someVariable = somethingNew will only result in a discernable change in how your script runs if something else references the someVariable which has the new value in it. Here, the number = number * 2 does nothing because nothing is referencing the doubled number variable; reassigning the parameter doesn't do anything to the array.
The way to do this with forEach would be to reassign the index in the array, just like in the for loop.
var numbers = [2,5,37,500,75];
function getDoubled(array) {
array.forEach(function(number, i){
array[i] = number * 2;
})
}
getDoubled(numbers);
console.log(numbers);
Alternatively, a nicer approach would be to construct a new array with the changed values, which can be done with .map, which creates a new array by performing a callback on every element of the original array - that way you don't have to look at the (unimportant) indicies to get the output you want.
var numbers = [2,5,37,500,75];
function getDoubled(array) {
return array.map(num => num * 2);
}
console.log(getDoubled(numbers));

Why is comparing an array and a number so slow in JavaScript? What is it doing?

I accidentally compared a large array and a number with <, and JavaScript locked up for over 5 seconds. What is the expected behavior of this comparison? Is it iterating over the whole array? MDN didn't clarify the situation.
As a concrete example, this code snippet takes over 5 seconds to print done:
var m = [];
m[268435461] = -1;
console.log('start');
if (m < 0) { }
console.log('done');
Javascript "arrays" (those with Array prototype, not typed arrays), are just objects, therefore this
var m = [];
m[268435461] = -1;
is exactly the same as
var m = {
"268435461": -1
}
except that in the first case, m has the Array prototype and a special length property.
However, methods defined in Array.prototype (like forEach or join) are trying to hide that fact and "emulate" sequential arrays, as they exist in other languages. When iterating their "this" array, these methods take its length property, increase the loop counter from 0 upto length-1 and do something with the value under the key String(i) (or undefined if there's no such key)
// built-in js array iteration algorithm
for (let i = 0; i < this.length - 1; i++) {
if (this.hasOwnProperty(String(i))
do_something_with(this[String(i)])
else
do_something_with(undefined)
Now, length of an array is not a number of elements in it, as the name might suggest, but rather the max numeric value of its keys + 1, so in your case, length will be 268435462 (check it!)
When you do m < 0, that is, compare a non-number to a number, JS converts them both to strings, and Array.toString invokes Array.join, which, in turn, uses the above loop to convert elements to strings and insert a comma in between:
// built-in js Array.join algorithm
target = '';
for (let i = 0; i < this.length - 1; i++) {
let element = this[String(i)]
if(element !== undefined)
target += element.toString()
target += ','
}
Illustration:
m = [];
m[50] = 1;
console.log(m.join())
This involves lots of memory allocations, and that's what is causing the delay.
(After some more testing, the allocation are not the deciding factor here, "hollow" loops will cause the same slowdown:
console.time('small-init')
var m = [];
m[1] = -1;
console.timeEnd('small-init')
console.time('small-loop')
m.forEach(x => null)
console.timeEnd('small-loop')
console.time('big-init')
var m = [];
m[1e8] = -1;
console.timeEnd('big-init')
console.time('big-loop')
m.forEach(x => null);
console.timeEnd('big-loop')
That being said, I don't think modern JS engines are that silly, and implement iterations exactly as described above. They do have array-specific optimizations in place, but these optimizations are targeted at "good" sequential arrays, and not at bizarre edge cases like this. Bottom line: don't do that!

Have for loop or a .forEach method remove the current array element, then have flow continue from from the same index?

I am working on this problem from coderbytes:
Using the JavaScript language, have the function SecondGreatLow(arr) take the array of numbers stored in arr and return the second lowest and second greatest numbers, respectively, separated by a space. For example: if arr contains [7, 7, 12, 98, 106] the output should be 12 98. The array will not be empty and will contain at least 2 numbers. It can get tricky if there's just two numbers!
My solution works by removing the greatest and lowest values from the array and then using Math methods to return the second highest and lowest values.
However, when there are two or more instances of the greatest or lowest elements of the array, and their index positions are adjacent to each other, I believe only the first instance of this value is removed and the flow skips over the second instance.
Is there any way to have the loop run through the same index value twice in order to process adjacent greatest or lowest values?
Here are the two iterations of my solution which I've tested.. my original attempt using .forEach and my second using a for loop.. I've console.logged a situation in which the code works and in which it doesn't for each attempt.
I'm really new to all this, almost a month of learning in my free time so explaining yourself as if I'm really dumb is appreciated. Thanks!!!
// * First attempt - using .forEach method *
// outputs the second lowest value in the array
function secondLowest (arr) {
var g = function () {
return Math.min.apply(null, arr);
}
arr.forEach(function (val, indx, arr) {
if (val === g()) {
arr.splice(indx, 1);
}
});
lowestVal = g(); // store this value to be added back in for the secondGreatest function (in case there were only two digits in the arr argument)
return Math.min.apply(null, arr);
}
// number trimmed from the array in the function secondLowest..
// to be added back in for the function secondGreatest
var lowestVal = 0
// adds back the lowest value which was trimmed..
// outputs the second greatest value
function secondGreatest (arr){
arr.splice(0,0,lowestVal);
var g = function () {
return Math.max.apply(null, arr);
}
arr.forEach(function (val, indx, arr) {
if (val === g()) {
arr.splice(indx, 1);
}
});
return Math.max.apply(null, arr);
}
// putting together the output
function SecondGreatLow (arr) {
return secondLowest(arr) + " " + secondGreatest(arr);
}
console.log(SecondGreatLow([1,2,3,4,5]));
console.log(SecondGreatLow([1,1,2,2,3,3,4,4,5,5]));
// * Second attempt - using for loops *
// outputs the second lowest value in the array
function secondLowest (arr) {
var g = function () {
return Math.min.apply(null, arr);
}
lowestVal = g();
for (var i = 0; i < arr.length; i++) {
if (arr[i] === g()) {
arr.splice(i, 1);
}
}
return Math.min.apply(null, arr);
}
// number trimmed from the array in the function secondLowest..
// to be added back in for the function secondGreatest
var lowestVal = 0
// adds back the lowest value which was trimmed..
// outputs the second greatest value
function secondGreatest (arr){
arr.splice(0,0,lowestVal);
var g = function () {
return Math.max.apply(null, arr);
}
for (var i = 0; i < arr.length; i++) {
if (arr[i] === g()) {
arr.splice(i, 1);
}
}
return Math.max.apply(null, arr);
}
// putting together the output
function SecondGreatLow (arr) {
return secondLowest(arr) + " " + secondGreatest(arr);
}
console.log(SecondGreatLow([1,2,3,4,5]));
console.log(SecondGreatLow([1,1,2,2,3,3,4,4,5,5]));
I tried using the delete operator in order to keep the argument array length consistent (rather than shortening it with splice which I think allows the adjacent value to pass into the removed element's index position and not be processed in the next runthrough of the for loop or forEach method) but the Math.min/max.apply methods don't like having 'undefined' in the array argument.
Also if my code is looking ugly/annoying and makes you cringe then please take this opportunity to vent.. helps me learn to write code that doesn't piss people off ;)
** Solution Found **
Thank you for reminding me of the sort method!(function?) Here's what I ended up with:
function SecondGreatLow (arr) {
var secondLow = 0,
secondHigh = 0;
arr.sort(function(a,b){
return a-b;
});
for (var i = 1; i < arr.length; i++) {
if (arr[i] !== arr[i-1]) {
secondLow = arr[i];
break;
}
}
for (var j = (arr.length-2); j >= 0; j--) {
if (arr[j] !== arr[j+1]) {
secondHigh = arr[j];
break;
}
}
return secondLow + " " + secondHigh;
}
console.log(SecondGreatLow([1,1,2,2,3,3,4,4,5,5]));
What an awesome community.. I'll be back with more questions and hopefully I'll feel confident enough to even answer some questions in the near future. Thanks!
I feel like perhaps I'm missing something, but the challenge doesn't seem to include a requirement for removing items from the original array, so I don't see why you're modifying it in such a way. The requirements you provided simply state to return 'a b' where a is the second lowest, and b the second highest.
So, I would first recommend sorting the list. Since you know you're working at the upper and lower bounds, you don't have to iterate over anything (nor should you). Your test arrays are already sorted, but ensuring order will make your code more robust and able to handle other inputs. Check out the Arrays API for more details.
While it seems it may be beyond the scope of your problem, you may also want to look into sorting algorithms to learn more about how that all works, rather than relying solely on the API.
Once sorted, you should be able to easily compare inwards from the boundaries to get your second lowest and second highest values.
Also, you shouldn't need to utilize the Math API, simple inequality operators should do the trick (< and >).
EDIT: While I recommend working on the problem yourself, here is a simple solution to the problem. I place it here so if you get stuck you can reference this (and the associated comments) for guidance.
function SecondGreatLow(arr) {
var i;
var j;
var lowest;
var highest;
var secondLowest;
var secondHighest;
//Sort Array
arr.sort(function (a, b) {
return a - b;
});
//Get Bounds
//Since we sorted the array, and the default sort is in
//ascending lexicographical order, then we're guaranteed that
//our 'lowest' value is at index 0 and our 'highest' value is
//at index arr.length -1. Note that these values may be
//equal.
lowest = arr[0];
highest = arr[arr.length - 1];
//Search for second lowest.
for (i = 0; i < arr.length; i++) {
if (arr[i] > lowest) {
secondLowest = arr[i];
break;
}
}
//If we reach the end of the array, but didn't
//find a greater value, then, since the array is sorted,
//we're guaranteed that all values in the array are equal.
//Therefore, the required value comparisons have no meaning,
//and we return 'undefined'.
if (secondLowest === 'undefined') {
return 'undefined';
}
//Search for second highest, working backwards from the
//high end of the array until we reach our crossover point
//with the previous search. Either some value > arr[i] is the
//second highest, or arr[i] is, so there's no point in looking
//at values in the indices lower than i.
for (j = arr.length - 1; j >= i; j--) {
if (arr[j] < highest) {
secondHighest = arr[j];
break;
}
}
return secondLowest + ' ' + secondHighest;
}
var result = SecondGreatLow([3,3,4,5,4,6]);
console.log(result);
JSFiddle
You may create a priority queue limited by 2 elements, then feed it with all the array and pop the value, which would be the answer.
The trivial implementation would look like:
function UniqueNElementSortedQueue(length, comparison) {
this.length = length;
this.data = [];
this.comparison = comparison;
}
UniqueNElementSortedQueue.prototype.push = function(v) {
if (this.data.indexOf(v) > -1) {
return;
}
this.data.push(v);
this.data.sort(this.comparison);
this.data.length = this.length;
};
UniqueNElementSortedQueue.prototype.popIfN = function() {
if (this.data.length == this.length) {
return this.data[this.length - 1];
}
};
JSFiddle: http://jsfiddle.net/fmfv67xy/
The solution is O(N) (one might argue that I have sorting internally and they would be right :-)) by number of operations and O(N) by additional memory (where N is linear to the "next-lowest/greatest" index value)
As the description does not define what to return if it was not sufficient data fed - my implementation returns undefined.
Actually, let me turn my comment into an answer, since I think it always helps to also worry about performance:
Create 4 local variables:
largest and second_largest initialized to a number smaller than anything you'd expect in your array, or to the smallest possible value that your data-type can take on (-2^31 - 1)
smallest and second_smallest initialized to a number larger than anything you'd expect in your array, or the largest possible value for your data-type (2^31)
Loop over your array once:
If you find a number larger than largest, set second_largest to largest and largest to that number
If you find something smaller than largest but larger than second_largest, set second_largest to that number
If you find a number smaller than smallest, set second_smallest to smallest and smallest to that number
If you find something larger than smallest but smaller than second_smallest, set second_smallest to that number
When you're done with your loop, your answer is contained in second_largest and second_smallest
Given how small your arrays seem to be, you might not notice much of a performance difference between this answer and the other suggested ones, but I think it's a good habit to get into to always keep this concern in the back of your head for every line of code you write. In this answer, you process every array element exactly once (i.e. the algorithm runs in O(n)), whereas adding a sorting step leads to every element being processed multiple times in a general case (the best sorting algorithms (Timsort, for example) have an expected runtime of O(n log n)).
One thing to note:
#elclanrs mentioned a special case in his comment ([1, 1]), which according to the definition of the problem does not have a defined solution (multiple 1s all would be considered the largest number, so there is no second-largest). In this case, the algorithm above will still have second_largest and second_smallest set to their initial values.

confused about indexOf(array[0])

This is for a coderbyte challenge that I finished a while ago. Your function is supposed to get the second greatest and second lowest numbers heres what I originally used:
function SecondGreatLow(arr){
var sorted = arr.sort(function(a,b){
return a-b;
});
return sorted[1] + " " + sorted[arr.length - 2];
}
SecondGreatLow(readline());
I got two cases wrong with this function one of them being ([2,2,2,5,5,5,6]) because there are duplicate numbers. I implemented this into my new code :
function SecondGreatLow(arr) {
var exclude = [arr[0]];
for(var i = 1; i < arr.length; i++) {
if (exclude.indexOf(arr[i]) == -1) {
exclude.push(arr[i]);
}
}
return exclude
}
SecondGreatLow([33,33,33,44,44,44,55,55,6,4,3])
My question is how does this find all the duplicate numbers? Isn't the variable exclude(arr[0]) 33? I'm confused how this gets rid of all the duplicate numbers.
My question is how does this find all the duplicate numbers?
It actually doesn't find duplicate values, it collects unique values.
You iterate over the original array and add only values to exclude which are not already in exclude. That guarantees that every value in exclude is unique.
My question is how does it do this if exclude originally is only arr[0] or 33
This line:
var exclude = [arr[0]];
it just used to not start with an empty array. As you can see in the for statement
for(var i = 1; i < arr.length; i++) {
the code starts iterating at index 1, not 0. You could also just have started with an empty array:
var exclude = [];
for(var i = 0; i < arr.length; i++) {
but that's a bit wasteful since you know that the first value is going to be added to exclude anyway.
The way this code works is but adding new variables into the array exclude. It will only add the new variable if the current variable doesn't already exist in the array exclude.
The line:
exclude.indexOf(arr[i]) == -1
is what is doing main work.
It finds variable i and check if it already exists in the array exclude. If it does not exist indexOf returns -1, thus the if statement is true, and we go into the line
exclude.push(arr[i]);
and add the variable into the array exclude.
In the below loop
for(var i = 1; i < arr.length; i++) {
if (exclude.indexOf(arr[i]) == -1) {
exclude.push(arr[i]);
}
}
It is checking if the number from input array is already present in exclude if not it will insert that number to exclude.
The indexOf() method searches the array for the specified item, and returns its position. so in case of duplicate numbers it will not return -1 thus the logic above will skip adding that duplicate number to exculde variable. You can refer below for more details.
http://www.w3schools.com/jsref/jsref_indexof_array.asp

Can I limit the length of an array in JavaScript?

I want to display the product browsing history, so I am storing the product ids in a browser cookie.
Because the list of history is limited to 5 items, I convert the cookie value to an array, then check the length of it and cut the redundant.
The code below is what I have tried, but it does not work; the array item isn't removed.
I would like to ask how to limit the array length so it can only store 5 items?
Or
How can I cut the items after the array index 4?
var id = product_id;
var browseHistory = $.cookie('history');
if (browseHistory != null) {
var old_cookie = $.cookie('history');
var new_cookie = '';
if (old_cookie.indexOf(',') != -1) {
var arr = old_cookie.split(',');
if (arr.length >= 5) {
arr.splice(4, 1)
}
}
new_cookie = id + ',' + old_cookie;
$.cookie('history', new_cookie, { expires: 7, path: '/' });
} else {
$.cookie('history', id, { expires: 7, path: '/' });
}
You're not using splice correctly:
arr.splice(4, 1)
this will remove 1 item at index 4. see here
I think you want to use slice:
arr.slice(0,5)
this will return elements in position 0 through 4.
This assumes all the rest of your code (cookies etc) works correctly
The fastest and simplest way is by setting the .length property to the desired length:
arr.length = 4;
This is also the desired way to reset/empty arrays:
arr.length = 0;
Caveat: setting this property can also make the array longer than it is: If its length is 2, running arr.length = 4 will add two undefined items to it. Perhaps add a condition:
if (arr.length > 4) arr.length = 4;
Alternatively:
arr.length = Math.min(arr.length, 4);
arr.length = Math.min(arr.length, 5)
var arrLength = arr.length;
if(arrLength > maxNumber){
arr.splice( 0, arrLength - maxNumber);
}
This solution works better in a dynamic environment like p5js. I put this inside the draw call and it clamps the length of the array dynamically.
The problem with
arr.slice(0,5)
is that it only takes a fixed number of items off the array per draw frame, which won't be able to keep the array size constant if your user can add multiple items.
The problem with
if (arr.length > 4) arr.length = 4;
is that it takes items off the end of the array, so which won't cycle through the array if you are also adding to the end with push().
I think you could just do:
let array = [];
array.length = 2;
Object.defineProperty(array, 'length', {writable:false});
array[0] = 1 // [1, undefined]
array[1] = 2 // [1, 2]
array[2] = 3 // [1, 2] -> doesn't add anything and fails silently
array.push("something"); //but this throws an Uncaught TypeError
I was surprised nobody mentioned the following snippet to limit the length of the array:
arr.splice(5);
According to the Parameters definitions for splice, if start is larger than the length of the array, it will be set to the length of the array, and if deleteCount is omitted or larger than the array length, all of the items after start will be deleted.
Therefore, if you want to limit an array to some MAX_SIZE (modifying the existing array instead of creating a new instance) an easy shortcut is just arr.splice(MAX_SIZE).
As others have said, there is more going on with the code in the question, but given the title and spirit of the ask, I hope this is a useful answer for anyone else ending up here via search.
Note: According to the compatibility notes for IE 5.5-8, deleteCount does not work as described above, so this solution won't work right on those browsers.
You need to actually use the shortened array after you remove items from it. You are ignoring the shortened array.
You convert the cookie into an array. You reduce the length of the array and then you never use that shortened array. Instead, you just use the old cookie (the unshortened one).
You should convert the shortened array back to a string with .join(",") and then use it for the new cookie instead of using old_cookie which is not shortened.
You may also not be using .splice() correctly, but I don't know exactly what your objective is for shortening the array. You can read about the exact function of .splice() here.
Came here but couldn't find a functional way of limiting the length of an array.
So I came up with:
const list = ["a","b","c","d","e","f","g","h","i"];
const listWithOnly3Items = list.filter((element,index) => index < 3);

Categories