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
Related
I'm looking for an easy way to do the following:
I have an array of random numbers between 0-99. I need two arrays, first which have values which share the same first digit, second for values that share the same second digit. It doesn't matter what that digit is, as long as it occurs more than once. 0-9 is to be treated as if the first digit was 0.
To illustrate the problem:
[0, 10, 20, 11, 19, 12, 54, 64, 23, 24] would result in [10, 11, 12, 19, 23, 24] and [0, 10, 20, 24, 54, 64]. Some values can go in both arrays if they meet the criteria.
I've found solutions that are useful if you know the digit you're comparing. However, not in this scenario.
I know the way to begin is to convert numbers inside the array into strings: array.map(String), so that the second and first digit can be accessed by first[0], second[1]. I don't know how to proceed from there, though. Any ideas are greatly appreciated.
You can group your array based on first digit and last digit in an object accumulator. Then, filter the grouped array based on length.
const input = [0, 10, 20, 11, 19, 12, 54, 64, 23, 24],
groupLastDigits = input.reduce((r, n) => {
const last = n % 10;
r[last] ??= [];
r[last].push(n);
return r;
}, {}),
groupFirstDigits = input.reduce((r, n) => {
let first = Math.floor(n / 10);
if(first === n) first = 0;
r[first] ??= [];
r[first].push(n);
return r;
}, {}),
getNumbers = o => Object.values(o).filter(arr => arr.length > 1).flat()
commonLastDigits = getNumbers(groupLastDigits),
commonFirstDigits = getNumbers(groupFirstDigits);
console.log(commonLastDigits);
console.log(commonFirstDigits);
I'd like to offer a generic solution for n digits!
let matchingDigitGroups = function*(arr) {
// Note that very large numbers will convert to string
// in scientific notation, breaking this approach
let maxDigits = Math.max(...arr.map(num => `${num}`.length));
let strs = arr.map(num => {
let str = `${num}`;
while (str.length < maxDigits) str = `0${str}`;
return str;
});
for (let i = 0; i < maxDigits; i++) {
let result = [];
for (let n = 0; n <= 9; n++) {
let matchDigits = strs.filter(str => str[i] === `${n}`);
if (matchDigits.length > 1) result.push(...matchDigits);
}
yield result;
}
};
let tests = [
[ 0, 10, 20, 11, 19, 12, 54, 64, 23, 24 ],
[ 100, 200, 300 ],
[ 111, 222, 333, 771, 828, 399 ],
[],
[ 1 ],
[ 92929, 91919 ]
];
for (let arr of tests) {
console.log(`Groups for: [ ${arr.join(', ')} ]`);
for (let group of matchingDigitGroups(arr)) console.log(` [ ${group.join(', ')} ]`);
}
This question already has answers here:
JavaScript: Rearrange an array in order – largest, smallest, 2nd largest, 2nd smallest, 3rd largest, 3rd smallest,
(4 answers)
Closed 2 years ago.
I need help with the sorting as i am totally confused what to do. This is the example:
[1, 65, 3, 52, 48, 63, 31, -3, 18, 56] to [-3, 65, 1, 63, 3, 56, 18, 52, 31, 48].
The first element has to be the smallest, the second to be biggest, the third to be the second smallest element, the fourth to be the second biggest element.
What i have tried is:
function solve(arr) {
var lowestNum = [...arr].sort((a,b) => a-b);
let biggestNum = [...arr].sort((a,b) => b-a);
let nums = [];
let i = 0;
while (nums.length !== arr.length) {
nums.push(lowestNum.shift()); lowestNum.pop();
nums.push(biggestNum.shift()); biggestNum.pop();
i++;
}
return nums;
}
console.log(solve([1, 65, 3, 52, 48, 63, 31, -3, 18, 56]));
However, that is not the way as my exercise requires me to sort.
This isnt the most elegant solution, but:
let a = [11, 2, 3, -22, 41];
function bigSmallSorter(arr) {
// Sort a copy of the passed in array from least to most.
let sortedArr = [...arr.sort((n, k) => n - k)];
let newArr = [];
let flip = true;
for( let i of arr ) {
let temp = flip
// Take first element of the sorted array
? sortedArr.shift()
// Take the last element of the sorted array
: sortedArr.pop();
// Add the newest value to the value that is ultimately returned
newArr.push(temp);
// Flip this so the next iteration takes from the other
// end of the array
flip = !flip;
}
// Profit
return newArr;
}
console.log(bigSmallSorter(a))
//>[ -22, 41, 2, 11, 3 ]
And an version of the method without comments:
let a = [11, 2, 3, -22, 41];
function bigSmallSorter(arr) {
let sortedArr = [...arr.sort((n, k) => n - k)]
let newArr = [];
let flip = true;
for( let i of arr ) {
let temp = flip
? sortedArr.shift()
: sortedArr.pop()
newArr.push(temp)
flip = !flip;
}
return newArr;
}
console.log(bigSmallSorter(a))
I think the example is the simplest.
I have this array : [10, 30, 55, 75, 94, 112] and the value 69.
I want to get: [55, 75, 94, 112]
So I want to filter out the smaller values but keep the closest.
an idea?
Something like this using filter.
var arr = [1, 3, 5, 7, 9, 11];
var value = 6;
function remove(arr, value) {
let copy = arr;
let newArr = arr.filter((arr, index) => !((arr < value) && (copy[index + 1] < value)))
console.log(newArr)
}
remove(arr, value) // [5, 7, 9, 11]
Just filter, and check if either this or the next value in the array is >= limit
const filter=(array, limit)=>array.filter((value,index)=>value>=limit||array[index+1]>limit);
console.log(filter([10, 30, 55, 75, 94, 112], 69));
Use Array.filter, Array.pop, Array.sort Array.concat
function f(val, array){
// array.sort( (a,b)=>a-b ); // if array isn't sorted, you must be use
return array.filter(e=>e>val).concat([array.filter(e=>e<val).pop()])
}
Given an input array arr and a value val:
Iterate over arr, splicing all elements greater than val into a separate array.
Append the maximum element left in arr to the new array.
arr = [1, 3, 5, 7, 9, 11];
val = 6;
new_arr = []; // Initialize new array
for (let i=0; i<arr.length; i++) { // Iterate over current array
if (arr[i]>val) { // If the current element is greater than the given value
new_arr.push(arr.splice(i, 1)[0]) // Splice it into the new array
i--; // Decrement i so as to not skip any elements
}
}
new_arr.unshift(Math.max(...arr)) // Add the closest value to the new array
console.log(new_arr);
Start by finding the closest delta for numbers that are under the limit. Then filter all numbers that are under the limit, and the their delta with the limit is not equal to the delta you've found in the previous step.
Note: this assumes that the numbers are unique, but they don't have to be sorted.
const fn = (arr, lim) => {
const closestSmallerDelta = arr.reduce((acc, n) =>
lim < n || lim - n > acc ? acc : lim - n
, Infinity)
return arr.filter(n => lim < n || lim - n === closestSmallerDelta)
}
console.log(fn([10, 30, 55, 75, 94, 112], 69));
// unsorted array
console.log(fn([112, 55, 75, 94, 10, 30], 69));
assuming the array is always sorted:
const deleteSmallerButLastOne = (array , refNum ) =>{
const finalArr = []
for(let [index, num] of array.entries()){
if(num < refNum && array[index + 1] >= refNum) finalArr.push(num)
if(num > refNum) finalArr.push(num)
}
return finalArr
}
I took this approach instead of modifying the original array just in case you need it for later.
Here's one possible approach (apparently assuming array is sorted). The idea is to find the very first item that is greater than or equal to lim; when you found it, there's no need to check the rest of an array (and that's what all the other answers do).
function processSorted(arr, lim) {
const i = arr.findIndex(el => el >= lim);
if (i === -1) // no elements greater than lim, return just the last one
return arr.slice(-1);
if (i === 0) // the first element is greater than lim, give back 'em all!
return arr;
return arr.slice(i - 1);
}
console.log(processSorted([10, 30, 55, 75, 94, 112], 69));
It's not sorted, it's possible to sort it anyway, or, if you really strive for n-only, go with one-iteration only approach:
function processUnsorted(arr, lim) {
const res = [];
let oneLess = -Infinity,
len = arr.length;
arr.forEach(el => {
if (el >= lim) res.push(el);
else oneLess = Math.max(oneLess, el);
});
if (oneLess !== -Infinity) res.unshift(oneLess);
return res;
}
console.log(processUnsorted([30, 55, 10, 94, 75, 112], 69));
I can't get a result I wanted:
Even Array is: 2, 18, 38, -10, 0, 104
Odd Array is: 3, 13, -5, 11
Two arrays where one contains even numbers another odd.
Here is the code:
let arrayMain = [2, 3, 13, 18, -5, 38, -10, 11, 0, 104];
let oddArray = [];
let evenArray = [];
for (let i = 0; i < arrayMain.length; i++){
if (i % 2 == 0) {
evenArray.push(arrayMain[i]);
}
else {
oddArray.push(arrayMain[i]);
}
}
console.log("Odd Array is " + oddArray);
console.log("Even Array is " + evenArray);
It gives me:
Odd Array is 3,18,38,11,104
Even Array is 2,13,-5,-10,0
How can I fix this?
You need to check the value, not the index.
if (arrayMain[i] % 2 == 0) {
// ^^^^^^^^^^ ^
Sup fellow geeks!
I'm trying to make an array that lists all the possible values of the sums of the elements of an array. I'm sure this must be quite easy but I'm up to 2 or 3 hours now and I'm getting frustrated, I think I'm almost there...
var frootVals = [0,1,2,3,4,5]
var frootInc = frootVals
var fruitEnd = frootInc[frootInc.length-1]//begins at 5
var fruitAll = 15 // The total of all the numbers in the array. (this is actually
// calculated in another function, but lets just say I declared it as 15)
for (e = frootVals.length-2 ;fruitEnd !== fruitAll;e--){ //so I want it to
//finish when the final array entry is 15.
for (p = 1;p < e; p++){
var incEnd = frootInc[frootInc.length-p]
frootInc.push(incEnd + frootVals[p]) //1st time round (5 + 1 = 6, 5 + 2 = 7,
//5 + 3 =8, 5 + 4 = 9) THEN start again with 9 now being incEnd so pushes
//9+1 = 10 etc etc until the last digit is 15 and the whole loop stops...
}
}
EDIT - Basically the final result I'm after is frootInc to be be an array of the integers [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] - I'm sure I'll kick myself for giving up but I've only been learning a few weeks so this is all quite brain taxing.
After thinking about your question a bit, I think the easiest solution would be with recursion when the condition that the final value added to the array is less than the sum of values.
Here's a JS Fiddle for demo: http://jsfiddle.net/ukgzwpky/
To break it down a bit (and so that you may confirm I have the question right :D), say we have the following array:
[0, 1, 2, 3, 10, 15, 30]
The sum of the values are: 61. So the expected output array would be:
[0, 1, 2, 3, 10, 15, 30, 31, 32, 33, 40, 45, 46, 47, 48, 55, 60, 61]
To further break it down, the looping logic would do something like this:
// Get final value to add to previous values
var val = [0, 1, 2, 3, 10, 15, 30].pop();
// Add the final value - 30 - to all previous values (ignoring the zero)
.. loop here and push the values to our array ...
// For the first iteration, the updated array looks like:
[0, 1, 2, 3, 10, 15, 30, 31, 32, 33, 40, 45]
// New values calculated from: 30 + 1, 30 + 2, 30 + 3, 30 + 10, 30 + 15
At this point, our max value of 61 is less than the final value of 45 So, we do it again!
var val = [0, 1, 2, 3, 10, 15, 30, 31, 32, 33, 40, 45].pop();
.. loop here and push the values to our array ...
// Second iteration, the updated array looks like:
[0, 1, 2, 3, 10, 15, 30, 31, 32, 33, 40, 45, 46, 47, 48, 55, 60, 61]
// New values are: 45 + 1, 45 + 2, 45 + 3, 45 + 10, 45 + 15
// Note that 45 + 30 would be greater than our sum of 61, so we break
If that's correct, here's a script that I wrote that populates such an array:
function getPopulatedArray(arr) {
var max = arguments[1] || getSum(arr),
current = arr.pop(),
temp = [],
i = 1,
len = arr.length;
// Populate temp array with values
for (; i < len; i++) {
if ((arr[i] + current) < max) {
temp.push(arr[i] + current);
} else {
temp.push(max);
break;
}
}
arr.push(current);
arr = arr.concat(temp);
// Done? Or should we continue?
if (arr[arr.length - 1] < max) {
return getPopulatedArray(arr, max);
} else {
return arr;
}
}
Again, the JS fiddle for demonstration: http://jsfiddle.net/ukgzwpky/
Hopefully this helps!
A very simple solution would be to do something like this:
var frootVals = [0,1,2,3,4,5]
var result = [];
for (var i = 0; i < frootVals.length; i++){ // Iterate over the array twice
for (var j = 0; j < frootVals.length; j++){ // To calculate all combinations
result.push(frootVals[i] + frootVals[j]);
}
}
Now, if you don't want duplicates, try this:
var frootVals = [0,1,2,3,4,5]
var result = [];
for (var i = 0; i < frootVals.length; i++){
for (var j = 0; j < frootVals.length; j++){
var value = frootVals[i] + frootVals[j];
if(result.indexOf(value) === -1){
result.push(value);
}
}
}
You could then use result = result.sort() if you want to output a sorted result.