Bucketing numbers in an array - javascript

I have a list of numbers e.g 1-to 60, but not necessarily in increments of 1. E.g 1-40 in increments of 1, and 40-60 in increments of two.
I have another set of defined numbers (buckets) –e.g 2, 3, 5 , 10, 30, 50
I need to produce a two dimensional array with percentages of where each number from above (1 to 60) fits into which bucket.
to make this simpler: let’s say we have numbers 1 to 10, and buckets, 2, 3, 5, 10
I want my two dimensional array to look like this:
I can do this with a bunch of conditionals, but I think there’s a solution out there which I have not thought of and would be great if someone could shed some light!
I need to do this in JavaScript, but any language would help me to try and understand any solution more optimal than lots of if’s deciding where each number fits and then doing (6-5/10-5)=0.2 for each cell.
I’m trying to avoid hardcoding buckets, 2, 3, 5, 10 so that any set of buckets or numbers can do the job.
EDIT:
First of all, I'm sorry for the incomplete description - I was on my phone at the time and couldn't post on stackoverflow via a computer.
Both 1-10 and 2,3,5,10 represent years. Effectively, I'm trying to bucket each year from 1 to 10.
Year 1 goes 100% into Bucket 2 - I guess there isn't a specific
formula for this cell
Year 2 goes 100% into Bucket 2 - No specific formula either
Year 3 goes 100% into Bucket 3 - 3==3
Year 4 is split half between Bucket 3, and half between Bucket 5. The formula for this is: (Year 4 - Bucket 3)/(Bucket 5 - Bucket 3) = 0.5
Year 5 goes 100% into Bucket 5.
Year 6, goes 80% into Bucket 5, and 20% into Bucket 10. The formula for this is: 1-(6-5)/(10-5)=0.8 and for its neighbouring cell (6-5)/(10-5)
...and so on...
I hope this makes it clearer.

You could do something like this. It puts it in exactly the format you asked for in the post:
function bucketize(numberList, buckets) {
// buckets must contain values sorted from smallest to largest
var bucketized = [];
var i, j, lowBucket, difference, bucketSpan, ratio;
for (i=0; i<numberList.length; i++) {
bucketized[i]=new Array(buckets.length + 1);
bucketized[i][0]=numberList[i];
lowBucketIndex=null;
for (j=0; j<buckets.length; j++) {
if (lowBucketIndex === null && numberList[i] < buckets[j]) {
lowBucketIndex=j-1;
if (lowBucketIndex < 0) {
// this bucket gets it all
bucketized[i][j+1]=1;
} else {
//divide this value between buckets
difference = numberList[i] - buckets[lowBucketIndex];
bucketSpan = buckets[j] - buckets[lowBucketIndex];
ratio=difference/bucketSpan;
bucketized[i][lowBucketIndex+1] = 1-ratio;
bucketized[i][j+1] = ratio;
}
} else {
bucketized[i][j+1]=0;
}
}
if (lowBucketIndex === null) {
bucketized[i][buckets.length] = 1;
}
}
return bucketized;
}
var buckets = [2,3,5,10];
var numberList=[1,2,3,4,5,6,7,8,9,10];
var bucketized = bucketize(numberList, buckets);
var i;
for (i=0; i<bucketized.length; i++) {
console.log(bucketized[i].join(','));
}
Here's a fiddle.

Related

Codewars Kata - 4th Kyu - Codewars style ranking system - "+1 problem"

I thought that this kata will be very cool and easy to get back to js after a break.
I was so wrong lol.
So the URL to kata is here with all of the logic and math informations, i'll put the necessary ones below.
URL:
https://www.codewars.com/kata/51fda2d95d6efda45e00004e/train/javascript
Code:
class User {
constructor() {
this.rank = -8;
this.progress = 0;
this.rankTable=[-8,-7,-6,-5,-4,-3,-2,-1,1,2,3,4,5,6,7,8];
this.rankIndicator=0;
}
incProgress(rankOfActivity) {
if (rankOfActivity == 0 || rankOfActivity > 8 || rankOfActivity < -8) throw new error("Rank input out of range");
if (rankOfActivity <= this.rank - 2) return;
let diff = this.rankTable.indexOf(rankOfActivity)-this.rankTable.indexOf(this.rank);
if (diff==0) this.progress+=3;
else if(diff==-1) this.progress+=1;
else if(this.rank!=8){
this.progress+= 10*diff*diff;
while(this.progress>=100 && this.rank<8){
this.progress-=100;
this.rankIndicator++;
this.rank=this.rankTable[this.rankIndicator];
}
}
if (this.rank==8) this.progress=0;
}
}
Completing an activity that is ranked one ranking lower than the user's will be worth 1 point
After i saw that, my point of view was:
If the difference between activity rank and user's rank was -1 ,for example:
User's rank is -2 (index 6 in rankTable array)
Activity ranked at -3 (index 5 in rankTable array)
The difference would be 5-6 = -1
So it should add up 1 point of progress, and it looks like it doesn't do that and i cant figure it out why it doesn't add up.
Here are bunch of errors to show that it happens on any rank.
After applying rank of -1 the progress was expected to be 21, but was actually 20
After applying rank of 3 the progress was expected to be 61, but was actually 60
After applying rank of 8 the progress was expected to be 51, but was actually 50
//...
//if (rankOfActivity <= (this.rank - 2)) return;
//bug
let diff = (this.rankTable.indexOf(rankOfActivity)) - (this.rankTable.indexOf(this.rank));
if (diff <=-2)return;//
//fixed
//...
Personally, i do not like array and indexOf use here. Using a rank counter of [0 to 15] would be a little better.

Find The Smallest - Codewars Challenge - Javascript

Trying to solve this Codewars challenge.
You have a positive number n consisting of digits. You can do at most one operation: Choosing the index of a digit in the number, remove this digit at that index and insert it back to another or at the same place in the number in order to find the smallest number you can get.
Task: Return an array or a tuple or a string depending on the language (see "Sample Tests") with:
1) the smallest number you got
2) the index i of the digit d you took, i as small as possible
3) the index j (as small as possible) where you insert this digit d to have the smallest number.
Example:
smallest(261235) --> [126235, 2, 0] or (126235, 2, 0) or "126235, 2, 0"
Other examples:
209917, [29917, 0, 1]
285365, [238565, 3, 1]
269045, [26945, 3, 0]
296837, [239687, 4, 1]
So, in order to get the smallest number possible, we will want to remove the smallest digit from the number and place it at the front of the number, correct?
function smallest (n) {
//turn n into an array
let array = String(n).split("").map(Number);
let smallest = Math.min(...array);
//find index of smallest in original array
let index = array.indexOf(smallest);
//remove smallest from original array, move it to front
array.splice(index, 1);
array.unshift(smallest);
let newNumber = Number(array.join(""));
//return array of new number, index of where the smallest was,
//and index of where the smallest is now
return ([newNumber, index, 0]);
}
console.log(smallest(239687));
My answer is returning the correct number, but, about half the time, it is not returning the correct index i and index j.
EDIT: Latest attempt:
function smallest (n) {
let array = Array.from(String(n)).map(Number);
let original = Array.from(String(n)).map(Number);
let sorted = Array.from(String(n)).map(Number).sort((a, b) => a - b);
let swapValueOne = [];
let swapValueTwo = [];
for (let i = 0; i < array.length; i++) {
if (array[i] !== sorted[i]) {
swapValueOne.push(sorted[i]);
swapValueTwo.push(original[i]);
break;
}
}
swapValueOne = Number(swapValueOne);
swapValueTwo = Number(swapValueTwo);
let indexOne = original.indexOf(swapValueOne);
let indexTwo = original.indexOf(swapValueTwo);
//remove swapValue
array.splice(indexOne, 1);
//insert swapValue
array.splice(indexTwo, 0, swapValueOne);
return ([Number(array.join("")), indexOne, array.indexOf(swapValueOne)]);
}
console.log(smallest(296837));
^ Sometimes it gives the correct number with the correct swap indices, and sometimes both the number and the swap indices are wrong.
Putting the smallest element in the front (let's call it a "greedy" solution) is non-optimal. Consider the case where n = 296837, as in your last test case. Your code returns [296837, 0, 0] because it finds that 2 is the smallest digit and it moves it to the front (does nothing, essentially). As your example illustrates, there's a better approach: [239687, 4, 1], that is, move 3 to the first index in the array.
You'll need to reformulate your strategy to be non-greedy to find a global optimum.
If you're still stuck, you can try the following:
Numbers can't contain that many digits--why not try every possible swap?
Here's a small idea that might help.
If you have a number like:
239687
The smallest number you can make with this is the sorted digits:
236789
In the original number, the 2 and the 3 are already in the correct position. If you start from the left of the number and the sorted number, the first difference you find is the number that needs to be swapped. It needs to be swapped with the corresponding number in the sorted list:
orig 2 3 9 6 8 7 -- 6 needs to go before 9
| | x
sorted 2 3 6 7 8 9
Above the next sorted digit is 6, but the original has 9. You need to insert 6 before 9.
For an algorithm you can sort your digits and find the index of the first difference (starting from the left). This is one of your return values (2 in the example). Now find the index of sorted[2] (ie. 6) in the original (index 3). Insert the value in you original array and you're done.
The approach of finding the first not sorted element doesnt solve correctly all the cases for example if the number is 300200, the first not sorted number is the 3 and if you put a 0 in that place, depending what 0 you move you got:
(0)30020
(0)30020
(0)30200
(0)30200
All of the answers are wrong because what you have to do is to put the 3 at the end of the number to get
(000)2003

javascript get random number: lower probability to get higher number in the interval

Ok, so I have very big array of numbers in javascript:
[1, 1.01, 1.02, 1.03, ..., 1.99, 2, ..., 9.98, 9.99, ..., 299.99, 300]
And what I need is to get one of them using random segment. So basically I need random number but the catch is that I need to get random using the lottery style. So the chance to get "1" will be 30 000 (very hight) and the chance to get 1.01 will be 29 999. But the chance to get 300 will be very low according of all numbers in this array.
I hope you will understand the problem and will help me to solve this. As I have mentioned before, this have to be made 100% randomly and I have no idea how to make it..
The solution I had so far:
I was trying to expanse the array by adding multiple same numbers and lower the count of it by each step. So I have added 30000 units of 1 and 29999 units of 1.01 ... and 2 units of 299.99 and one unit of 300. But the array got very large and I came here to find better solution.
Also I have found this: https://stackoverflow.com/a/13758064/5786106
and it seems to be the answer to me but I don't know how to use it with the decimal system (0.01, 0.02, ... 0.99)
var num = Math.pow(Math.floor(Math.random()*10), 2);
One solution would be to make the very large array you propose, but to make it imaginary, without ever constructing that object in code.
How long will the imaginary array be? Well your array has (300 - 1) * 100 + 1 = 29,901 elements in it. Then there are (29,901 + 1) * (29,901 / 2) = 447,049,851 elements in the imaginary array. So the first step is to generate a random integer between 0 and 447,049,850:
var imaginaryIndex = Math.floor(Math.random() * 447049851);
The next step is to determine which real index in your original array corresponds to the imaginaryIndex in the imaginary array.
var indexFromEnd = 0;
while((indexFromEnd + 2) * ((indexFromEnd + 1) / 2) < imaginaryIndex)
indexFromEnd++;
Finally, you need to calculate the value of the element in your array based on where it is in your array:
return 300 - (indexFromEnd * 0.01);
Now let's clean that up and put it in a nice, reusable function:
function triangularWeightedRandomSelect(myArray){
var imaginaryIndex =
Math.floor(Math.random() * (myArray.length + 1) * myArray.length / 2);
var indexFromEnd = 0;
while((indexFromEnd + 2) * ((indexFromEnd + 1) / 2) < imaginaryIndex)
indexFromEnd++;
return myArray[myArray.length - 1 - indexFromEnd];
}

In JavaScript, get high numbers in a set of numbers, based on variance

I have JavaScript code that grabs the highest part of a series of numbers.
Let's say I have a series of 10 numbers, such as 1, 3, 4, 4, 7, 15, 16, 16, 30, 31. Let's say I want to grab the highest 25% (rounding up), then I end up with 16, 30, 31. But just by looking at the results, the 16 doesn't "belong". Ideally I'd like to get a result of 30, 31 instead. (Note that this is just an example; in reality, I have sets of hundreds of numbers, and their numbers are pretty random.)
Is there a better way to grab the highest portion of a series of numbers, based on variance? And then all I'd have to do is specify the value of the variance until I get the numbers I want from multiple sets of numbers.
var sequence = [14, 28, 20, 11, 7, 15, 18]; //all the numbers
var generalCap = Math.round(sequence.length / 4); //25%
var currenthighs = [];
function calculatehighs(strictness) {
for(var i=0;i<sequence.length;i++) {
var current = sequence[i];
if(currentHighs.length > generalCap - 1) {
var match = false;
for(var n=0;n<currenthighs.length && !match;n++) {
if(current > currenthighs[n]) {
currenthighs.splice(n); //delete one
currenthighs.push(current);
match = true;
}
}
} else {
currenthighs.push(current); //if under 25% simply put it in the array
}
}
for(i=0;i<currenthighs.length;i++) {
var difference = 0;
for(n=0;n<currenthighs.length;n++) {
difference += Math.abs(currenthighs[i] - currenthighs[n]);
}
if(difference > currenthighs[i] / strictness) {
currenthighs.splice(i);
}
}
}
Here this might work.
I guess you want around top 25% of the set, and also the variance should be low. Define a function of variance of the subset and its size. (eg. *variance+abs(sizeOfSubset-idealSize) ).
Now start from smallest acceptable subset and iterate upto largest acceptable subset. Then pick subset that minimizes your function. The key here is to pick the function correctly, it will have a significant effect on your results. Also, you might want to normalize the variance as variances of larger numbers will be larger than that of smaller numbers. So, a sample objective function (i.e. the function to be minimized) could be
variance/mean + abs(sizeOfSubset-idealSize)/totalSize.

Javascript - return array of values that totals next number up

I am trying to work out the best way to achieve the following:
A score might require a total of 25 'items' AS A MINIMUM
currently that person might have 15.8 'items'
I have a number of items required to reach that score (9.2)
So to get that score the minimum a person must have is 10 'items' in x weeks (to be 25.8 and over the 25 threshold).
Assuming they have 4 weeks to do it that gives 2.5 needed per week.
What I am struggling with is how to output an array of 'whole items' that will make 10.
e.g.
2
3
2
3
I want the items to be as evenly distributed as possible.
so
1
2
3
4
would be useless
I am just trying to work out the best way to do this.
I was thinking of:
finding nearest whole number ROUNDED UP (3 in this example)
Then outputting that number
Then on the next pass gathering the 'remainder' (-0.5) and then rounding the number.
But it doesn't quite work for all cases.
Can anyone help me get my head around it without writing hundreds of lines of code.
As a further example say 17 were needed in 5 weeks that would be
4
3
3
4
3
Thanks in advance.
You could do it some way like this:
function myFunction(total, weeks) {
var n = 0;
var temp = 0;
var arr = [];
while (n < total) {
temp = n;
n += total / weeks;
arr.push(Math.floor(n) - Math.floor(temp));
}
return arr;
}
myFunction(10, 4); //[2, 3, 2, 3]
myFunction(17, 5); //[3, 3, 4, 3, 4]
In this code, total / weeks will be the average number you'll want to add to n to get from 0 to total in exactly weeks iterations.
Now the difference between the original value of n (which is stored in temp) and the new value of n is pushed to the array. That will give you the rounded numbers you'll need to add up to the entered total.

Categories