Using _.countBy with ranges - javascript

I have a list of FICO scores that I'd like to group depending on their value, with groups starting at 0-620, then 620-640, incrementing by 20 every time.
What it should return is a count of values in each group -- 0-620, 620-640, 640-660, 660-680, etc. up to 780-800.
I'm currently using _.countBy but it returns a count for every unique number, rather than for every group.
var counts = _.countBy(ficos, function(x) { return x });
//Counts should be {620: 22, 625: 19, 655: 24, 670: 20, 690: 30, 720: 29, 734: 17, 760: 21, 790: 18}
Is there a way to take advantage of _.countBy or some other function? I'm trying to avoid a long if statement, if possible.

So return the appropriate groups then:
function (x) {
if (x < 620) return '<620';
if (x >= 800) return '>800';
var lower = Math.floor(x / 20) * 20,
higher = lower + 20;
return lower + '-' + higher;
}

Related

why JavaScript is behaving differently

why it is showing me 36 even though the minimum number is 27
var combination = [27, 36]
for (let x in combination) {
if (combination[x] < 50) {
var min = Math.min(combination[x])
}
}
console.log(min)
i tried this multiple ways like
var combination = [27, 30, 40, 44, 3, 239, 329, 2, 5, 20923, 96]
for (let x in combination) {
if (combination[x] < 50) {
var min = Math.min(combination[x])
}
}
console.log(min) //output-- 5 //it should be 2
in this third example i add (-) to 2
var combination = [27, 30, 40, 44, 3, 239, 329, -2, 5, 20923, 96]
for (let x in combination) {
if (combination[x] < 50) {
var min = Math.min(combination[x])
}
}
console.log(min) // output-- still 5 // it should be -2
again when am adding (-) to other numbers like in -96 or -5 the output was okay (-96) but when im adding (-) to 2 it is not showing me -2 in the output instead it showing me 5
not only in javascript i tried this with lua, php but output was same as js
can anyone explain me why this happen and how solve this
You're not comparing values to determine the minimum, but instead just replacing the min variable with the last number in the array that is smaller than 50. This can be fixed as follows:
let min = undefined;
for (let x in combination) {
if (combination[x] < 50) {
min = min == undefined ? combination[x] : Math.min(min, combination[x])
}
}
Using filter and reduce, this can be made a lot shorter:
combination.filter(x => x < 50).reduce((x, y) => Math.min(x, y))

fast way to check a number is in a range of a group of numbers

I just encounter a scenario like the following. It sounds a little bit like a leetcode question.
Support I can get a list of number in a pattern, to simplify my questions.
For example, [0, 5, 10, 15, 20, 25] or [0,7,14,21], it's already sorted.
then given a range (I can guarantee range will never overlap between two consecutive numbers)
for example, range=2;
If num=11, then I supports to get index of 10 because 11 is in a range of 10-2 to 10+2.
If num=12.5, then I will just return -1 or anything else to indicate we does not find.
I can simply go through the list and check if the number is in the range of each number but I feel like there is a O(1) solution since the list itself has some pattern exist. any help is greatly appreciated.
Diff is also provided, the above example diff=5.
I does not encounter any performance issue now with O(N) list checking, just want to make thing better.
You could use Array.some to check if the number (and range) overlaps with your list, for example:
This will be O(n), and I suspect you will need a very large list indeed to justify creating an O(1) type solution.
We can go the other way, creating an array of candidate numbers (numbers within the range, e.g. for 10 +- 2 this would be [8,9,10,11,12]. NB: This approach will not work for floating point values.
We check each number for membership of a set created from the list. This will still technically be O(n), but N will most likely be small (e.g. 5).
let list = [0, 5, 10, 15, 20, 25];
// This solution will need at most N iterations, where N is the length of list
function checkInRange(value, range, list) {
return list.some((el) => {
return (el >= (value - range)) && (el <= (value + range));
})
}
// This solution will need at most N iterations, where N is the length of a, e.g. 2 * range + 1
function checkInRangeSet(value, range, list) {
// Create an array of matching numbers, e.g. 8,9,10,11,12
let a = Array.from({ length: 2*range + 1 }, (v,k) => value - range + k);
let set = new Set(list);
return a.some((el) => {
return set.has(el);
})
}
console.log("Solution with simple loop");
console.log(checkInRange(11, 1, list));
console.log(checkInRange(10, 0, list));
console.log(checkInRange(30, 5, list));
console.log(checkInRange(9, 0, list));
console.log(checkInRange(100, 20, list));
console.log("Solution with Set");
console.log(checkInRangeSet(11, 1, list));
console.log(checkInRangeSet(10, 0, list));
console.log(checkInRangeSet(30, 5, list));
console.log(checkInRangeSet(9, 0, list));
console.log(checkInRangeSet(100, 20, list));
Here's my shot at an O(1) solution.
const list = [0, 7, 14, 21, 28];
const interval = list[1];
const range = 2;
function getIndex(num, range) {
const halfItvl = interval / 2;
const inRange = Math.abs((num % interval) - halfItvl) >= halfItvl - range;
return inRange ? Math.trunc((num + halfItvl) / interval) : -1;
}
// Test for all numbers within the list
for (var i = 0; i < list[list.length - 1]; i++) {
const res = getIndex(i, range);
if (res === -1) {
console.log(`${i} is out of range ${range}`);
} else {
console.log(`${i} is within range ${range} of index ${res}`);
}
if (i % interval === interval - 1) {
console.log("---------------------");
}
}
Actually, it seems a good bit simpler if I establish the index first. Then all we need to do is subtract the current number from the value at the found index, take its absolute value, and see if it's less than or equal to the given range number.
const list = [0, 7, 14, 21, 28];
const interval = list[1];
const range = 2;
function getIndex(num, range) {
const idx = Math.trunc((num + (interval / 2)) / interval);
return Math.abs(list[idx] - num) <= range ? idx : -1;
}
// Test for all numbers within the list
for (var i = 0; i < list[list.length - 1]; i++) {
const res = getIndex(i, range);
if (res === -1) {
console.log(`${i} is out of range ${range}`);
} else {
console.log(`${i} is within range ${range} of index ${res}`);
}
if (i % interval === interval - 1) {
console.log("---------------------");
}
}

generating intervals multiples of 10 between two numbers

I have two numbers like ESP1 and ESP2.
In my DB there are many data to the ESP1 & ESP2, ESP1 is minimum values and ESP2 is maximum value.
i need to generate intervals between those two number in multiplies of 10, for example.
ESP1 = 0 and ESP2 = 83
my result should be [0,10,20,30,40,50,60,70,80].
how to write code for this in js
Just use a for loop:
function generateIntervalsOf(interval, start, end) {
const result = [];
let current = start;
while (current < end) {
result.push(current);
current += interval;
}
return result;
}
generateIntervalsOf(10, 0, 83) // [0, 10, 20, 30, 40, 50, 60, 70, 80]
Another solution
const a = 0;
const b = 83;
const result = _.range(a, b, 1).filter(item => item%10 == 0);
console.log(result)
Use either a for or while loop. There are two approaches you could take.
A: Brute force. Count up from the start number to the end number and store every multiple of ten.
const results = [];
for (let i = ESP1; i <= ESP2; i++) {
if (i %10 === 0) {
results.push(i);
}
}
B: Cleverness. Brute force might be slow if you were doing this over a very large range. Instead of going through every single number, we only need the multiples of ten. So, in short, we just need the next multiple of ten after ESP1, and we add ten until we exceed ESP2.
e.g.
const results = [];
const floor = ESP1 + 10 - ESP1 % 10;
// i.e. if ESP1 is 13, add 10 to get 23, and then subtract 3 to get 20
for(let i = floor; i < ESP2; i+= 10) {
results.push(i);
}
If we ran this with ESP1 = 3 and ESP2 = 56, we would get the result [10, 20, 30, 40, 50]. This approach will go through 1/10 as many iterations as approach A, since we're counting by tens.

Return an image name based on an integer without using if-else

How do I return an image name based on an integer? I have an idea but it uses loads of if and else cases.
So here's the deal, I need a function which would return the name of an image based on an integer between 1 and 500. If the integer is between 1 and 30, I display image1 and from this point on I return a different image at 50, 75, 100, 125, 150, 175, 200, ...etc.
The first idea that comes to mind is using IFs and Elses like that:
if (number >=1 && number <= 29) {
Return image1
} else if (number >=30 && number <=49) {
Return image2
} else if... etc
But this function will become huge since I'll have around 20 If Else in order to cover all possible outcomes until number 500.
The other option is to create a JSON object with key: value pairs and get the image using the integer key like this:
returnImage(int) {
let images = {
30: 'image1',
50: 'image2',
75: 'image3'
}
return images[int]
}
Sadly I'm not sure how to make this work if the integer is 37 for example. I could obviously list all integers as keys but then the function would be even bigger than the if else function + there's gonna be a lot of repetition so that doesn't seem to work.
Any recommendations?
You can use a mathematical calculation for this:
function returnImage(i) {
let num = 1;
if (i > 30)
num = parseInt((parseInt(i / 25, 10) * 25 - 1) / 25, 10) + 2;
return 'image' + num;
}
console.log(returnImage(10));
console.log(returnImage(30));
console.log(returnImage(49));
console.log(returnImage(50));
console.log(returnImage(74));
console.log(returnImage(100));
console.log(returnImage(125));
console.log(returnImage(130));
console.log(returnImage(200));
console.log(returnImage(300));
console.log(returnImage(490));
Just loop through the keys in images.
returnImage(num){
let images= {
30: 'image1',
50: 'image2',
75: 'image3'
}
for(let image in images){
if(num <= image) return images[image];
}
}
EDIT: I would add some default image after the loop in the case that the number exceeds the max valued image.
What you need is a range.
const IMAGE_FOR_NUMBER = [
[0, 9, "one.jpg"],
[10, 19, "ten.jpg"],
[20, 100, "many.jpg"],
];
function getImage(n, notFound="not-found.jpg") {
for (const [min, max, image_name] of IMAGE_FOR_NUMBER) {
if ((min <= n) && (n <= max)) return image_name;
}
return notFound;
}
Note that it works for disjoint ranges, too

Given a degree x, find the nearest degree in an array of degrees

I have an array of degrees, [10, 90, 200, 280, 355] for a circle.
I'm given a degree, let's say 1. How do I determine that 1 is closest to 355 degrees?
Subtract the two numbers. If the difference is larger above 180 [or below -180], subtract [or add] 360. Now you can just compare absolute values of the difference.
Here is an actual formula:
degreediff = min(abs(x-y),360-abs(x-y))
This is more compact and efficient:
function difference(a, b) {
var d = Math.abs(a - b);
return d > 180 ? 360 - d : d;
};
function closest(a, bs) {
var ds = bs.map(function(b) { return difference(a, b); });
return bs[ds.indexOf(Math.min.apply(null, ds))];
};
> difference(1, 355)
6
> closest(1, [10, 90, 200, 280, 355])
355
You have one value which will contain the closes degree found found_degree and one for the actual difference degree_difference.
Next, iterate over the whole array and calculate two values: abs(degree_at_position - target_degree) and abs(degree_at_position - 360 - target_degree). If one of those values is smaller than degree_difference, you have a closer degree - store it in found_degree and update degree_difference accordingly.
That's it.
You maybe should initialise found_degree with -1 and degree_difference with 360, just to make sure you can properly interpret the result in case of an empty given array as well - or you simply handle the case of an empty input array separately.
Is this a homework, by the way?
The brute force approach would be something like this:
var closestElement;
var closestDivergence = 360;
var toCompare = 355;
var choices = [1, 90, 200, 280, 355];
for(i=0;i<choices.length;i++){
var currentDivergence=choices[i] - toCompare;
if (currentDivergence<0) {
currentDivergence+=360;
}
if (currentDivergence < closestDivergence){
closestDivergence = currentDivergence;
closestElement = i;
}
}
if (closestElement != NaN){
alert('Closest value is '+choices[closestElement]);
}
Here's a nice little quickie
function closest(deg,ar) {
return ar.sort(function(a,b){var c = deg; return Math.min(360 - (a-c),Math.abs(a-c)) - Math.min(360 - (b-c),Math.abs(b-c))})
}
var myArray = [355, 280, 200, 181, 90, 30];
alert(closest(180,myArray));
Sorts and returns the array according to which one is closest to the provided degree. Index 0 is closest. It does wrap making 355 closer to 0 than 10.
First check the array (check which element is closest) using the given degree (1 in your example), then add 360 and check with that degree (361). Compare which result is better:
x the given degree, y the first result, z the second result
if (abs(x-y) < 360+x-z)
choose y;
else
choose z;
If the array is sorted you can check it with a binary sort which gives you O(log n) time in worst case scenario. Otherwise you have to browse through the whole array two times.
This formula will only work with circles. It's pseudo code of course.
degree diff = min(abs(x-y),360-abs(x-y))
Using the comments from this page I managed to come up with this code:
function closest(deg, degs) {
deg = (deg / 360 > 1 ? deg - (Math.floor(deg / 360)*360) : deg);
var difference = 360;
var closest = -1;
for(i=0;i<degs.length;i++) {
var x = degs[i];
var diff = Math.min(Math.abs(x-deg),360-Math.abs(x-deg))
if(diff <= difference) {
closest = i;
difference = diff;
}
};
return closest;
}
closest(1000, [10, 90, 200, 280, 355]);

Categories

Resources