Related
I am trying to write a simple "getRandomIndex" function to return a random and unused index number for a given array but I get "Maximum call stack size exceeded" error at let rndIdx = Math.floor(Math.random() * (arr.length - 0)) + 0;.
const arr = [81, 33, 45, 22, 97, 19, 60];
const usedIndexes = []; // I am trying to store used indexes here
function getRandomIndex() {
let rndIdx = Math.floor(Math.random() * (arr.length - 0)) + 0;
if(usedIndexes.includes(rndIdx)) {
getRandomIndex();
return;
} else {
usedIndexes.push(rndIdx);
}
return rndIdx;
}
// if the array length is more than 10, I always get "call stack size exceed" error.
for(let i = 0; i < arr.length; i++) {
// I need to use all elements in the array eventually
console.log(getRandomIndex());
}
What I am missing?
You must check that the length of the array allows for different unused numbers.
if (usedIndexes.length>=arr.length) {
return null
}
You need to return the output of getRandomIndex() rather than calling it, dumping the output, then returning nothing (which gives you undefined)
Fixed Code
const arr = [81, 33, 45, 22, 97, 19, 60];
const usedIndexes = []; // I am trying to store used indexes here
function getRandomIndex() {
let rndIdx = Math.floor(Math.random() * (arr.length - 0)) + 0;
if(usedIndexes.includes(rndIdx)) {
// used to be
// - getRandomIndex();
// - return;
return getRandomIndex();
} else {
usedIndexes.push(rndIdx);
}
return rndIdx;
}
// if the array length is more than 10, I always get "call stack size exceed" error.
for(let i = 0; i < arr.length; i++) {
// I need to use all elements in the array eventually
console.log(getRandomIndex());
}
Output
0
2
4
3
1
6
5
I was given this problem at one of my interviews and was told I have 20 minutes to solve it. This is the answer I came up with ( 2 versions ). Can you let me know which version you prefer and why, and if you have a better idea of how to solve it (less complex, less memory usage, etc.) Please share.
Problem: You have an array of random numbers that range from 0 to 100 elements.
Write a function that will split this array into several arrays, each containing elements in the following range: (0-10],(10-20],(20-30], etc up to a 100].
Write a function that outputs these arrays in a form of a simple graph, where each delimiter represents a single value in the array.
Array = [10, 12, 71, 52, 51, 1, 5, 22, 21, 6, 95, 11, 3, 64, 45, 55,
65, 42, 99, 4];
Desired outcome:
5 Elements in array: ***** - 1,5,6,3,4
3 Elements in array: *** - 10,12,11
2 Elements in array: ** - 22,21
No Elements in array.
2 Elements in array: ** - 45,42
3 Elements in array: *** - 52,51,55
2 Elements in array: ** - 64,65
1 Elements in array: * - 71
No Elements in array.
2 Elements in array: ** - 95,99
// Version 1
arr = [10, 12, 71, 52, 51, 1, 5, 22, 21, 6, 95, 11, 3, 64, 45, 55, 65, 42, 99, 4];
const splitArray = (inputArray, range) => {
const newArray = [];
do {
let tempArray = [];
tempArray = inputArray.filter((item) => {
if (item >= range && item < range + 10) return item;
});
range += 10;
newArray.push(tempArray);
} while (range + 10 <= 100);
return newArray;
};
const printArrays = (array, delimiter) => {
let toPrint = "";
for (index in array) {
let stars = array[index].length;
let string = "";
for (let i = stars; i > 0; i--) {
string += delimiter;
}
toPrint += stars
? `${stars} Elements in array: ${string} - ${array[index]} \n`
: "No Elements in array. \n";
}
return toPrint;
};
console.log(printArrays(splitArray(arr, 0), "*"));
// Version 2
arr = [10, 12, 71, 52, 51, 1, 5, 22, 21, 6, 95, 11, 3, 64, 45, 55, 65, 42, 99, 4];
const getArrays = (inputArray) => {
const newArray = [];
let min = 0;
let max = 10;
do {
const tempArray = [];
for (i in arr) {
let val = arr[i];
val >= min && val < max ? tempArray.push(val) : "";
}
min += 10;
max += 10;
newArray.push(tempArray);
} while (max <= 100);
return newArray;
};
const printArrays = (array, delimiter) => {
for (index in array) {
let stars = array[index].length;
let string = "";
for (let i = stars; i > 0; i--) {
string += delimiter;
}
console.log(
stars ? `${stars} Elements in array: ${string} - ${array[index]}` : "No Elements in array."
);
}
};
printArrays(getArrays(arr), "^");
Both approaches have moderate issues.
The first approach does
let tempArray = [];
tempArray = inputArray.filter((item) => {
if (item >= range && item < range + 10) return item;
});
Better to just declare the tempArray as the filtered array to begin with.
const tempArray = inputArray.filter(...
Also, return item is suspicious inside a filter - all the filter callback cares about is whether its return value is truthy or falsey. Returning the array item when you actually want to indicate that the value should be included in the output is a common mistake. It happens not to be a problem here because 0 isn't a possibility, but it's still confusing. A better choice would be to do
const tempArray = inputArray.filter(
item => item >= range && item < range + 10
);
(and maybe rename range to startOfRange)
Both of your approaches are also iterating through the entire input array multiple times (once for each range), which seems a bit wasteful - better to iterate through the input once.
Your second approach uses for (i in arr), and both approaches are doing for (index in array). This is a bad idea, and since you don't actually care about the index you're iterating over, it'd make sense to use for..of loops instead.
I think a better looking approach that iterates through the input just once would be:
const arr = [10, 12, 71, 52, 51, 1, 5, 22, 21, 6, 95, 11, 3, 64, 45, 55, 65, 42, 99, 4];
const getArrays = (inputArray) => {
const grouped = {};
for (let i = 0; i < 100; i += 10) {
grouped[i] = [];
}
for (const item of inputArray) {
const rangeProp = Math.floor(item / 10) * 10;
grouped[rangeProp].push(item);
}
return Object.values(grouped);
};
const printArrays = (groupedArrays, delimiter) => {
for (const array of groupedArrays) {
const stars = delimiter.repeat(array.length);
console.log(
stars
? `${array.length} Elements in array: ${stars} - ${array.join(',')}`
: "No Elements in array."
);
}
};
printArrays(getArrays(arr), "*");
I will do that this way :
This approach is simple: it retrieves the values one by one and adds them to the array corresponding to their range.
const arr = [10, 12, 71, 52, 51, 1, 5, 22, 21, 6, 95, 11, 3, 64, 45, 55, 65, 42, 99, 4];
let ranges = arr.reduce((a,x)=>
{
let range = (x/10)|0 // get range start value 0 to 9
a[range] ??= [] // create the array of if it does not already exist
a[range].push(x)
return a
},{})
console.log('ranges=', ranges ) // so that noobs can visualize this result
for (let r = 0; r < 10; r++ )
{
if (!ranges[r])
document.write('No Elements in array.<br>')
else
{
let count = ranges[r].length
document.write(`${count} Elements in array: ${'*'.repeat(count)} - ${ranges[r].join(',')}<br>`)
}
}
.as-console-wrapper {max-height: 100% !important; width:20%; top: 0;
margin-left: 80%; }
.as-console-row::after {display: none !important;}
range = (x/10)|0 // get range start value 0 to 9
example in case of x = 25 -> 25/10 give 2.5 and 2.5 | 0 give 2 -> integer part value of 2.5
| is the OR boolean operator, work only on integers values so it return an interger
??= is Logical nullish assignment
I want to write a function that takes array as an argument and returns how many numbers can be divided by 12. However, if array has a number higher than 111 then it should return 0;
I wrote this:
function isDivisble(array) {
let numberOfNum = 0;
for(let i=0; i < array.length; i++) {
if(array[i] % 12 == 0 && array[i] < 111) {
numberOfNum = numberOfNum + 1 ;
} else {
numberofNum = 0;
}
}
return console.log(numberOfNum);
}
let test = [12, 24, 36, 44, 55, 255];
isDivisble(test)
I realized that this code checks individually whether the current number is divisible and not higher than 111 and not globally whether the array has a number higher than 111, but I dont understand how to make a general array check.
Is writing for loop with if statement to check and then another for loop inside if statement makes it a little bit spaghetti?
You can use some to check if there is any element which is greater than 111 first.
Then you can use filter to get element that is divisible by 12.
Like this:
const isDivisible = (arr) => {
if (arr.some((e) => e > 111)) return 0;
return arr.filter((e) => e % 12 === 0).length;
};
const test = [12, 24, 36, 44, 55, 48];
console.log(isDivisible(test));
I've slightly modified your function that returns 0 if a number is greater than 111 else it checks if it is divisible by 12
function isDivisble(array) {
let count = 0;
for(let i=0; i<array.length; i++){
if(array[i] > 111){
return 0;
}else{
if(array[i] % 12 === 0){
count++
}
}
}
return count;
}
let test = [12, 24, 36, 44, 55, 255];
console.log(isDivisble(test));
The some array method will do the trick:
array.some(value => { return value > 111 }) will return true if any of its values is greater than 111.
You can also check if every array value respects a certain condition by using array.every with a similar callback:
array.every(value => { return value <= 111 }) is true only if every value in the array is lower than or equal to 111.
The best generic solution is the oene below
Logic
Filter the array for number greater that 111. If this filter returns an array with a length greater than 1 then return 0
Else filter the array for numbers divisible by 12. No need to check number is less than 111. Because if that number is greater than 111 the function might have already returned 0.
Retun the length of the above filter.
Working Fiddle
function isDivisbleGeneric(arr) {
const numberGreaterThanLimit = arr.filter((node) => node > 111);
let returnCount;
if(numberGreaterThanLimit.length > 0) {
returnCount = 0;
} else {
const divisibleBy12 = arr.filter((node) => node %12 === 0);
returnCount = divisibleBy12.length;
}
return returnCount;
}
let test = [12, 24, 36, 44, 55, 255];
const count = isDivisbleGeneric(test);
console.log(count);
One Line Solution
const isDivisbleOneLine = (arr) => arr.filter((node) => node > 111).length > 0 ? 0 : arr.filter((node) => node %12 === 0).length;
let test = [12, 24, 36, 44, 55, 255];
const count = isDivisbleOneLine(test);
console.log(count);
function isDivisble(array) {
// at the beginning, do from here
if (array.some(p=>p > 111)) {
return 0;
}
// Rest xxxxxx
}
Im learning Javascipt and actually im on episode with array methods.
My imaginary exercise relies on found the Max/Min value in array by array.find method.
Acutally I did smth like that, but script returned me "Undefined".
Please help. :)
const scores = [10, 20, 30, 22, 25, 109, 90];
const maxScore = scores.find(score => {
let max = 0;
for (let i=1; i < scores.length; i++){
if(score[i] > max){
max = score[i];
};
};
return max;
});
console.log(maxScore);
P.S. I know about "Math.max.apply", but I have to do it by array.find and simple loop.
You could take a closure over an index for looping from the end and a temporary max value which is at start undefined and gets the first value from the first element.
Then loop while the value at temp index is smaller than score, store this value in max, repeat.
At the end return the result if index plus one is equal to the temp index.
This approach takes a single loop. find iterates from start of the array and the inner loop from the end of the array if both indices cross, the result is found.
const
scores = [100, 20, 30, 22, 25, 109, 90],
maxScore = scores.find(
((j, max) => (score, i, array) => {
if (max === undefined) {
max = score;
j = array.length;
}
if (score < max) return;
while (array[j - 1] < score) max = array[--j];
return i + 1 === j;
})
()
);
console.log(maxScore);
The simplest way to do it, without using any Array methods, can be written as:
const maxScore = (scores) => {
let score = 0;
for ( let i = 0; i < scores.length; i++ ) {
if(scores[i] > score) {
score = scores[i]
}
}
return score;
}
From MDN:
The find() method returns the value of the first element
in the provided array that satisfies the provided testing function.
Lets redefine our simple function again,
const maxScore = scores => {
let score = Number.NEGATIVE_INFINITY;
scores.forEach(element => {
let acc = scores.find(number => number > score);
if(!isNaN(acc)) {
score = acc;
}
})
return score;
}
find works on each array element. So take the max outside the find method & log max. Besides there were two typos
const scores = [10, 20, 30, 22, 25, 109, 90];
let max = 0;
const maxScore = scores.find((score) => {
for (let i = 1; i < scores.length; i++) {
if (scores[i] > max) {
max = scores[i];
};
};
return max;
});
console.log(max)
Try this:
const scores = [10, 20, 30, 22, 25, 109, 90];
let max = 0;
scores.find(score => { if(score > max) max = score });
console.log(max);
Your current code is looping the scores array whilst its already looping it, JavaScripts .find, essentially loops the array.
const scores = [10, 20, 30, 22, 25, 109, 90];
scores.reduce(function(a,b) { return a > b ? a : b });
// 109
Where is the mistake? I want to sum every number in the array. The alert says NaN.
var numbers = [10, 42, 5, 87, 61, 34, 99];
var i = 0;
var e = 0;
while(i <= numbers.length) {
e = e + numbers[i];
i++;
}
alert(e);
This line is the reason:
while(i <= numbers.length) {
Arrays are 0 index so you can go from index 0 (inclusive) until numbers.length (exclusive). You are going beyond that limit, causing you to access an element that isn't defined at the given index. You must do this instead:
while(i < numbers.length) {
Alternatively using the ES2015 syntax you can do it like this.
let numbers = [10, 42, 5, 87, 61, 34, 99];
let sum = numbers.reduce((a,b) => a + b);
You can read about Array.prototype.reduce(accumulator, element, callback, startingValue) here.
Your condition is wrong, just use < insteead of <=
while(i < numbers.length)
There's a few ways you can improve this. The first is that you want to change your condition to i < numbers.length, not i <= numbers.length. As you've written it, the last number will never be counted.
Another way you can improve this is using the += notation -- there's no need to write e = e + numbers[i] when you can write e += numbers[i].
You could íterate from the end of the array with a post increment and a check for truthyness in the while condition.
Inside just add the item.
var numbers = [10, 42, 5, 87, 61, 34, 99],
i = numbers.length,
e = 0;
while (i--) {
e += numbers[i];
}
console.log(e);
The problem is in array length You are checking because array index starts with 0. In that way when doing:
i <= numbers.length
You are checking [0: 10, 1: 42, 2: 5, 3: 87, 4: 61, 5: 34, 6: 99, 7:???] because numbers length is 7. To solve it just do:
i < numbers.length
where You don't check non existing number in array.
Here is how we were thought in school:
//main code//
var numbers = [10, 42, 5, 87, 61, 34, 99];
var result = sumIntArray(numbers);
//end of main code//
function sumIntArray(array){
var result = 0;
for (var i = 0; i < array.length; i++) {
if (array[i].isInteger()) {
result += array[i];
} else {
if (i === 0) {
alert("1st object in array is not number, will skip...");
}
else if (i === 1) {
alert("2nd object in array is not number, will skip...");
}
else if (i === 2){
alert("3rd object in array is not number, will skip...");
}
else {
alert((i+1).toString() + "th object in array is not number, will skip...");
}
}
}
return result;
}
The for loop adds this i++ You make in a while loop and makes code a bit more clear.
No need for declaring i in main code :)
Always make functions to simplify main code.
Make function that will never make mistake even if somebody wants to break The logic.
Although don't make school break Your creativity, Your code is great! :)