JavaScript - Print a Histogram from a given array - javascript

// Given an array of integers [2, 1, 2, 101, 4, 95, 3, 250, 4, 1, 2, 2, 7, 98, 123, 99, ...]
I'm trying to Write a function (with linear run-time complexity) to print the following tabular output with ‘xxx' that resembles a histogram (the output should closely match the sample output below, including "99+" to capture the count for all numbers > 99):
Num | count
1 | xx
2 | xxxx
3 | x
4 | xx
98 | x
99 | x
99+| xxx

const dict = {}; // Empty dictionary
var min = Number.MAX_VALUE;
const maxRange = 5; // elements above maxRange will be clubbed in the same range.
//var arr = [2, 1, 2, 101, 4, 95, 3, 250, 4, 1, 2, 2, 7, 98, 123, 99];
const arr = [1, 2, 5, 3, 2, 2, 1, 5, 5, 6, 7, 1, 8, 10, 11, 12];
// iterate the array and set and update the counter in map
arr.forEach(function(num) {
min = Math.min(min, num); // find min
if (num > maxRange) {
num = maxRange + 1;
}
dict[num] = dict[num] ? dict[num] + 1 : 1;
});
console.log("Num | Count");
// Print the occurrences per item in array starting from min to max
while (min <= maxRange + 1) {
if (!dict[min]) { // print only those numbers which are defined in dictionary
min++;
continue;
}
var xArr = []
var range = dict[min];
for (i = 0; i < range; i++) {
xArr.push('x');
}
var disp = (min <= maxRange) ? (min + " | " + xArr.join("")) : (maxRange + "+ | " + xArr.join(""));
console.log(disp);
min = min + 1;
}

You might want to try iterating over the array using the forEach() method. then create an array of "x"'s as long as the current item in the array. Then use the join method to join the array into a string of x's.
I haven't included how to handle numbers over 99 but I think you this should be enough to get you started. The solution would involve using a conditional statement to check if the number is above 99 and printing accordingly.
i got the following output from my example:
Num | Count
2 ' |' 'xx'
4 ' |' 'xxxx'
6 ' |' 'xxxxxx'
8 ' |' 'xxxxxxxx'
have fun!
var arr = [2,4,6,8]
printHistogram = (array) => {
console.log("Num", '|', "Count")
array.forEach((x, i) => { //iterate over the array (x = current item, i = index)
var arrToJoin =[] //create an empty array
for(i = 0; i < x; i++) {
arrToConcat.push('x') //add an "x" to the array
}
console.log(i, ' |', arrToConcat.join(''))
})
}
printHistogram(arr)

How about this?
create an array of all the unique numbers called arrToCompare
then iterate over that array and compare each number to each number in the original array. If they are equal push an x to the array to join. Join it and log it with the appropriate symbols.
var arr = [7, 7, 8, 9, 2,4,6,8,2]
printHistogram = (array) => {
var arrToCompare =[]
console.log("Num", '|', "Count")
array.forEach((x, i) => {
arrToCompare.includes(x) ? "" : arrToCompare.push(x)
})
arrToCompare.forEach(function(x) {
var arrToJoin = []
array.forEach(function(i) {
if(i === x) {
arrToJoin.push('x')
}
})
console.log(x, '|', arrToJoin.join(''))
})
}
printHistogram(arr)

Related

Why am I getting wrong results when dividing numbers into arrays with weight percentage?

I have number of users to allocate to number of computers instances like docker or AWS. user can increase the number of instances and also can change the users. The instances have weight percentage.
Users: 10
Locations: 2 [{loc1.weight: 70%}, {loc2.weight: 30%}] so it's simple to have 7 and 3 users for each.
The total percentage might not be equal to hundred so scale factor can be anything.
My other requirement is each instance must have at least 1 user. I have condition applied that minimum user can not be less than locations so that part is good.
Other requirement is all users should be integers.
Example
Case #1
Users: 5
Locations: 4 where 1.weight = 15, 2.weight = 30, 3.weight = 15, 4.weight = 50 (total weight 110%)
Expected Results
Locations:
1.users = 1,
2.users = 1,
3.users = 1,
4.users = 2
Case #2
Users: 10
Locations: 4 where 1.weight = 10, 2.weight = 10, 3.weight = 90, 4.weight = 10 (total weight 120%)
Expected Results
Locations:
1.users = 1,
2.users = 1,
3.users = 7,
4.users = 1
Case #3
Users: 5
Locations: 2 where 1.weight = 50, 2.weight = 50
Expected Results
Locations:
1.users = 3,
2.users = 2
That was all of the explanation of the problem. Below is what I had tried.
function updateUsers(clients, weights) {
let remainingClients = clients;
const maxWeight = weights.reduce((total, weight) => total + parseInt(weight), 0);
let r = [];
weights.forEach(weight => {
let expectedClient = Math.round(clients * (weight / maxWeight));
let val = remainingClients <= expectedClient ? remainingClients : expectedClient;
remainingClients -= expectedClient;
r.push(val > 0 ? val : 1);
});
if ( remainingClients > 0 ) {
r = r.sort((a, b) => a > b ? 1 : -1);
for ( let i = 0; i < remainingClients; i++ ) {
r[i] = r[i] + 1;
}
}
return r;
}
I get good results for some numbers like
updateUsers(12, [5, 5, 5, 90]);
gives
[1, 1, 1, 9]; //total 12 users
but using very odd figures like below
updateUsers(12, [5, 5, 5, 200]);
returns
[2, 1, 1, 11]; //total 15 users which is wrong
At first get percentage, You said that in every quota should at least have 1 user, So we used Math.floor(), If its equal to 0, we return 1 and update userCount like so 1 - percentage.
const sumProcedure = (sum, n) => sum + n;
function updateUsers(userCount, weights) {
let n = userCount,
totalWeight = weights.reduce(sumProcedure),
results = weights.map(weight => {
let percentage = (weight * userCount) / totalWeight,
floor = Math.floor(percentage);
if (floor == 0) {
userCount -= 1 - percentage;
return 1
}
return floor;
}),
remain = n % results.reduce(sumProcedure);
while (remain--) {
let i = weights.indexOf(Math.max(...weights));
weights.splice(i, 1);
results[i]++
}
return results;
}
console.log(updateUsers(5, [50, 50])); // [3 , 2]
console.log(updateUsers(12, [5, 5, 5, 90])); // [1, 1, 1, 9]
console.log(updateUsers(12, [5, 5, 5, 200])); // [1, 1, 1, 9]
console.log(updateUsers(5, [15, 30, 15, 50])); // [ 1, 1, 1, 2 ]
console.log(updateUsers(10, [10, 10, 90, 10])); // [ 1, 1, 7, 1 ]
console.log(updateUsers(55, [5, 5, 5, 90])); // [ 3, 2, 2, 48 ]; It has 2 remainders
This approach works if speed is not super important. I don't know javascript, so a bit of it is going to be in pseudocode. I'll keep your notations though.
Let wSum = sum(weights) be the sum of all weights and unitWeight = wSum / weights.length be the weight each user should be assigned if all were given equal weight. Now, let
r[i] = 1;
weights[i] -= unitWeight;
for i = 0, 1 ... weights.length-1. This ensures that all locations receive at least 1 user and the weights are updated to reflect their 'remaining' weight. Now
remainingClients = clients - weights.length;
Assign the rest of the clients via a while(remainingClients > 0) loop or similar:
while(remainingClients > 0)
{
var indexMax = argMax(weights);
weights[indexMax] -= unitWeight;
r[indexMax] += 1;
remainingClients -= 1;
}
This gives the expected result for all your examples. The argMax should of course just return the index of the array corresponding to the maximum value. Due to argMax, the runtime becomes O(n^2), but it doesn't sound like you have tens of thousands of users, so I hope that's okay.

Javascript: Split an array according to a pattern: items 1, 5, 10, then 2, 6, 11, then 3, 7, 12

I am trying to split an array which has a repeating pattern of elements 1, 2, 3, and 4. I want to turn my array [1,2,3,4,5,6,7,8,9,10] into four arrays: [1,5,10], [2,6,11], [3,7,12], and [4,8,13]. I tried using multiples, but the result creates the new arrays in a wrong order. Here is my attempt:
var upload_names_and_ids = [
"Certificat de salaire", //first line is the upload's visible title
"certificat-de-salaire", //second line is the upload's id
"no-info-circle", //third line is the info-circle class
"", //fourth line is the info-circle text
"Allocations Familiales",
"alloc-familiales",
"no-info-circle",
"",
"Courrier Impot (déclaration précédente)",
"courrier-impot",
"info-circle right",
""
];
//Seperate our first array into 4
var upload_names = [];
var upload_ids = [];
var upload_info_circle_class = [];
var upload_info_circle_content = [];
for (var i=0; i<upload_names_and_ids.length; i++){
if (i%4==0) {
upload_info_circle_content.push(upload_names_and_ids[i]);
} else if (i%3==0) {
upload_info_circle_class.push(upload_names_and_ids[i]);
} else if (i%2==0) {
upload_names.push(upload_names_and_ids[i]);
} else {
upload_ids.push(upload_names_and_ids[i]);
}
}
Any help is much appreciated, thank you!
You could take a remainder with index and wanted length.
const
array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
length = 4,
result = array.reduce(
(r, v, i) => (r[i % length].push(v), r),
Array.from({ length }, _ => [])
);
console.log(result);
If you like to use predeclared array directly, you could replace this line
Array.from({ length }, _ => [])
with
[upload_names, upload_ids, upload_info_circle_class, upload_info_circle_content]
where the accumulator of Array#reduce keeps the object references.
It's not i%3==0 (which matches 0, 3, 6, …) but i%4==1 (to match 1, 5, 10, …). Same for i%2==0.
I would add a helper sliceN that takes an array and a positive integer. Then returns an array of arrays where the inner arrays are of length n.
sliceN([1,2,3,4,5,6,7,8,9], 3) //=> [[1,2,3], [4,5,6], [7,8,9]]
sliceN([1,2,3,4,5,6], 2) //=> [[1,2], [3,4], [5,6]]
Then also add a helper transpose that transposes a matrix.
transpose([[1,2,3], [4,5,6], [7,8,9]]) //=> [[1,4,7], [2,5,8], [3,6,9]]
transpose([[1,2], [3,4], [5,6]]) //=> [[1,3,5], [2,4,6]]
With these two helpers you can create the wanted result with ease.
const upload_names_and_ids = [
"Certificat de salaire", //first line is the upload's visible title
"certificat-de-salaire", //second line is the upload's id
"no-info-circle", //third line is the info-circle class
"", //fourth line is the info-circle text
"Allocations Familiales",
"alloc-familiales",
"no-info-circle",
"",
"Courrier Impot (déclaration précédente)",
"courrier-impot",
"info-circle right",
""
];
const [
upload_names,
upload_ids,
upload_info_circle_class,
upload_info_circle_content,
] = transpose(sliceN(upload_names_and_ids, 4));
console.log(upload_names);
console.log(upload_ids);
console.log(upload_info_circle_class);
console.log(upload_info_circle_content);
function sliceN(array, n) {
const slices = [];
for (let i = 0; i < array.length; i += n) {
slices.push(array.slice(i, i + n));
}
return slices;
}
function transpose(rows) {
if (rows.length == 0) return [];
const columns = rows[0].map(cell => Array.of(cell));
for (let iRow = 1; iRow < rows.length; iRow += 1) {
for (let iCol = 0; iCol < columns.length; iCol += 1) {
columns[iCol].push(rows[iRow][iCol]);
}
}
return columns;
}
If you are already use a library with helper functions chances are that one or both of these data transforming methods are present. sliceN can often be found as something with split, slice or chunk in the name. transpose is very specific and if present will probably be present under the same name.
As an example Ramda offers both these methods.
R.transpose(R.splitEvery(4, upload_names_and_ids))

How to shift all the items in an array to the right(by 1) using method map?

i have no idea on how to solve this problem. How can i shift all the items in an array by one, using the map method?
function shift(arr) {
// code
}
const b = [{ value: "" }, 8, "left"];
console.log(shif(b));
The output should be ["left", { value: "" }, 8];
Thank you very much to everyone!
You could subtract one from the index and add the lenght to prevent negative values, get the reaminder with the length and take it as index for the element for mapping.
function shift(arr) {
return arr.map((_, i, a) => a[(i + a.length - 1) % a.length]);
}
const b = [{ value: "" }, 8, "left"];
console.log(shift(b));
map may not be the most appropriate here, but if you insist,
let arr = [{value: ""}, 8, "left"];
let shifted = arr.map((_, i, a) => a[(i + 1) % a.length]);
console.log(shifted);
Here is an example how you could shift/rotate to right from 0 places to above without using any Array built in functions.
If the places are greater than the array size reduce it to number of places within the array size.
As for the main part you need to worry about the transition from the last index to the starting one if(i + places > list.length - 1)otherwise just put the element onto the new position defined by the place argument.
const list = [4, 3, 2, 10, 12, 1, 5, 6];
function rightShift(list, places) {
const result = [];
if (places > list.length) {
places = places % list.length;
}
for (let i = 0; i < list.length; i++) {
if (i + places > list.length - 1) {
result[places - (list.length - i)] = list[i];
} else {
result[i + places] = list[i];
}
}
return result;
}
//Shift right 1 place
console.log(rightShift([{ value: "" }, 8, "left"], 1));
//Shift right 154 places
console.log(rightShift([4, 3, 2, 10, 12, 1, 5, 6], 154));

Even Position Element

I am having difficulties solving the following math problem:
'Write a JS function that finds the elements at even positions in an array.
The input comes as an array of number elements.
The output must be displayed into element with id result like a text/string.'
Input : [1, 2, 3, 4, 5, 6, 7, 8, 9]
Output: 1 x 3 x 5 x 7 x 9
This is my code until now:
function evenPosition(arr) {
let evenIndexes = [];
let oddIndexes = [];
for (let i = 1; i < arr.length + 1; i++) {
if (i % 2 !== 0) {
oddIndexes.push(i)
} else {
evenIndexes.push(i)
}
}
}
evenPosition([1, 2, 3, 4, 5, 6, 7, 8, 9])
I cannot sort the elements though as it is shown in the output... Can you guys please help?
Is this you want?
function evenPosition(arr) {
let str = "";
for (let i = 0; i < arr.length; i++) {
if (i % 2 !== 0) {
str += " x ";
} else {
str += arr[i];
}
}
return str;
}
console.log(evenPosition([1, 2, 3, 4, 5, 6, 7, 8, 9]))
The example in the OP is incorrect. In the array [1, 2, 3, 4, 5, 6, 7, 8, 9], the odd values are at even indexes and the even values are at odd indexes. So the result of replacing the values at even indexes should be "x 2 x 4 x 6 x 8 x".
There are of course many ways to achieve the required outcome, e.g.
// Return string with values at even indexes replaced by "x"
function evenPosition(arr) {
let acc = 'x';
return arr.slice(-(arr.length - 1)).reduce(
(acc, curr, i) => acc += ' ' + (i%2? 'x' : curr), acc
);
}
// Indexes start at 0, so even numbers are at odd indexes
let a = [1,2,3,4,5,6,7,8,9];
console.log('Input: ' + a + ' Output: ' + evenPosition(a));
// Array with ellisions (sparse)
let b = [1,,,,5,6,,,9,10];
console.log('Input: ' + b + ' Output: ' + evenPosition(b));
// Using map and join
console.log('Input: ' + a +
' Output: ' + (a.map((v, i) => i%2? v : 'x').join(' ')));
You can use array's join method to convert array into string with the character's you want to join. Something like this.
function evenPosition(arr) {
let evenArr = [];
for (let i = 0; i < arr.length; i++) {
if (i % 2 === 0) {
evenArr.push(arr[i]);
}
}
return evenArr.join(" x ");
}
console.log(evenPosition([1, 2, 3, 4, 5, 6, 7, 8, 9]))
If you want to sort the array also then you can return something like this.
return evenArr.sort().join(" x ");

How to write the code with less time complexity for finding the missing element in given array range?

My function should return the missing element in a given array range.
So i first sorted the array and checked if the difference between i and i+1 is not equal to 1, i'm returning the missing element.
// Given an array A such that:
// A[0] = 2
// A[1] = 3
// A[2] = 1
// A[3] = 5
// the function should return 4, as it is the missing element.
function solution(A) {
A.sort((a,b) => {
return b<a;
})
var len = A.length;
var missing;
for( var i = 0; i< len; i++){
if( A[i+1] - A[i] >1){
missing = A[i]+1;
}
}
return missing;
}
I did like above, but how to write it more efficiently??
You could use a single loop approach by using a set for missing values.
In the loop, delete each number from the missing set.
If a new minimum value is found, all numbers who are missing are added to the set of missing numbers, except the minimum, as well as for a new maximum numbers.
The missing numbers set contains at the end the result.
function getMissing(array) {
var min = array[0],
max = array[0],
missing = new Set;
array.forEach(v => {
if (missing.delete(v)) return; // if value found for delete return
if (v < min) while (v < --min) missing.add(min); // add missing min values
if (v > max) while (v > ++max) missing.add(max); // add missing max values
});
return missing.values().next().value; // take the first missing value
}
console.log(getMissing([2, 3, 1, 5]));
console.log(getMissing([2, 3, 1, 5, 4, 6, 7, 9, 10]));
console.log(getMissing([3, 4, 5, 6, 8]));
Well, from the question (as it's supposed to return a single number) and all the existing solution (examples at least), it looks like list is unique. For that case I think we can sumthe entire array and then subtracting with the expected sum between those numbers will generate the output.
sum of the N natural numbers
1 + 2 + ....... + i + ... + n we can evaluate by n * (n+1) / 2
now assume, in our array min is i and max is n
so to evaluate i + (i+1) + ..... + n we can
A = 1 + 2 + ..... + (i-1) + i + (i+1) + .... n (i.e. n*(n+1)/2)
B = 1 + 2 + ..... + (i-1)
and
C = A - B will give us the sum of (i + (i+1) + ... + n)
Now, we can iterate the array once and evaluate the actual sum (assume D), and C - D will give us the missing number.
Let's create the same with each step at first (not optimal for performance, but more readable) then we will try to do in a single iteration
let input1 = [2, 3, 1, 5],
input2 = [2, 3, 1, 5, 4, 6, 7, 9, 10],
input3 = [3, 4, 5, 6, 8];
let sumNatural = n => n * (n + 1) / 2;
function findMissing(array) {
let min = Math.min(...array),
max = Math.max(...array),
sum = array.reduce((a,b) => a+b),
expectedSum = sumNatural(max) - sumNatural(min - 1);
return expectedSum - sum;
}
console.log('Missing in Input1: ', findMissing(input1));
console.log('Missing in Input2: ', findMissing(input2));
console.log('Missing in Input3: ', findMissing(input3));
Now, lets try doing all in a single iteration (as we were iterating 3 times for max, min and sum)
let input1 = [2, 3, 1, 5],
input2 = [2, 3, 1, 5, 4, 6, 7, 9, 10],
input3 = [3, 4, 5, 6, 8];
let sumNatural = n => n * (n + 1) / 2;
function findMissing(array) {
let min = array[0],
max = min,
sum = min,
expectedSum;
// assuming the array length will be minimum 2
// in order to have a missing number
for(let idx = 1;idx < array.length; idx++) {
let each = array[idx];
min = Math.min(each, min); // or each < min ? each : min;
max = Math.max(each, max); // or each > max ? each : max;
sum+=each;
}
expectedSum = sumNatural(max) - sumNatural(min - 1);
return expectedSum - sum;
}
console.log('Missing in Input1: ', findMissing(input1));
console.log('Missing in Input2: ', findMissing(input2));
console.log('Missing in Input3: ', findMissing(input3));
Instead of sorting, you could put each value into a Set, find the minimum, and then iterate starting from the minimum, checking if the set has the number in question, O(N). (Sets have guaranteed O(1) lookup time)
const input1 = [2, 3, 1, 5];
const input2 = [2, 3, 1, 5, 4, 6, 7, 9, 10];
const input3 = [3, 4, 5, 6, 8];
function findMissing(arr) {
const min = Math.min(...arr);
const set = new Set(arr);
return Array.from(
{ length: set.size },
(_, i) => i + min
).find(numToFind => !set.has(numToFind));
}
console.log(findMissing(input1));
console.log(findMissing(input2));
console.log(findMissing(input3));
If the array is items and the difference between missing and present diff is 1:
const missingItem = items => [Math.min(...items)].map(min => items.filter(x =>
items.indexOf(x-diff) === -1 && x !== min)[0]-diff)[0]
would give complexity of O(n^2).
It translates to: find the minimum value and check if there isn't a n-diff value member for every value n in the array, which is also not the minimum value. It should be true for any missing items of size diff.
To find more than 1 missing element:
([Math.min(...items)].map(min => items.filter(x =>
items.indexOf(x-diff) === -1 && x !== min))[0]).map(x => x-diff)
would give O((m^2)(n^2)) where m is the number of missing members.
Found this old question and wanted to take a stab at it. I had a similar thought to https://stackoverflow.com/users/2398764/koushik-chatterjee in that I think you can optimize this by knowing that there's always only going to be one missing element. Using similar methodology but not using a max will result in this:
function getMissing(arr) {
var sum = arr.reduce((a, b) => a + b, 0);
var lowest = Math.min(...arr);
var realSum = (arr.length) * (arr.length + 1) / 2 + lowest * arr.length;
return realSum - sum + lowest;
}
With the same inputs as above. I ran it in jsperf on a few browsers and it is faster then the other answers.
https://jsperf.com/do-calculation-instead-of-adding-or-removing.
First sum everything, then calculate the lowest and calculate what the sum would be for integers if that happened to be the lowest. So for instance if we have 2,3,4,5 and want to sum them that's the same as summing 0,1,2,3 and then adding the lowest number + the amount of numbers in this case 2 * 4 since (0+2),(1+2),(2+2),(3+2) turns it back into the original. After that we can calculate the difference but then have to increase it once again by the lowest. To offset the shift we did.
You can use while loop as well, like below -
function getMissing(array) {
var tempMin = Math.min(...array);
var tempMax = tempMin + array.length;
var missingNumber = 0;
while(tempMin <= tempMax){
if(array.indexOf(tempMin) === -1) {
missingNumber = tempMin;
}
tempMin++;
}
return missingNumber;
}
console.log(getMissing([2, 3, 1, 5]));
console.log(getMissing([2, 3, 1, 5, 4, 6, 7, 9, 10]));
console.log(getMissing([3, 4, 5, 6, 8]));
My approach is based on in place sorting of the array which is O(N) and without using any other data structure.
Find the min element in the array.
Sort in place.
Again loop the array and check if any element is misplaced, that is the answer!
function getMissing(ar) {
var mn = Math.min(...ar);
var size = ar.length;
for(let i=0;i<size;i++){
let cur = ar[i];
// this ensures each element is in its right place
while(cur != mn +i && (cur - mn < size) && cur != ar[cur- mn]) {
// swap
var tmp = cur;
ar[i] = ar[cur-mn];
ar[cur-mn] = tmp;
}
}
for(let i=0; i<size; i++) {
if(ar[i] != i+mn) return i+mn;
}
}
console.log(getMissing([2, 3, 1, 5]));
console.log(getMissing([2, 3, 1, 5, 4, 6, 7, 9, 10]));
console.log(getMissing([3, 4, 5, 6, 8]));

Categories

Resources