Reputation: 188
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), "^");
Upvotes: 1
Views: 247
Reputation: 22320
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
Upvotes: 0
Reputation: 370819
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), "*");
Upvotes: 3