I am trying to generalize the following function that I have implemented:
/**
* Calculates an interval for the given age.
*
* @memberof module:Users/Functions
* @function getAgeInterval
* @param {number} age - The age of the user.
* @param {number} [minimumAge=18] - The minimum age.
* @param {number} [range=10] - The range.
* @throws {Error} The given age must be greater or equal than the minimum age.
* @returns {string} The age interval.
*/
export default (age, minimumAge = 18, range = 10) => {
if (age < minimumAge) {
throw new Error(
"The given age must be greater or equal than the minimum age.";
);
}
const start = Math.floor((age - 1) / range) * range + 1;
const end = start + range - 1;
const interval = `${Math.max(start, minimumAge)}-${end}`;
return interval;
};
Basically, in this method, I group the age of my users using a minimum age and a range. Here is an example:
const getAgeInterval = (age, minimumAge = 18, range = 10) => {
if (age < minimumAge) {
throw new Error(
"The given age must be greater or equal than the minimum age."
);
}
const start = Math.floor((age - 1) / range) * range + 1;
const end = start + range - 1;
const interval = `${Math.max(start, minimumAge)}-${end}`;
return interval;
};
//
// MAIN
//
for (let age = 18; age < 100; age += Math.round(Math.random() * 10)) {
console.log(`${age}: ${getAgeInterval(age)}`);
}For now, the method is only working for “ages”. But I suppose it is possible to make it work with any type of numbers, (i.e. the total followers counter of a user).
Users might have different number of followers, and I need to group it reusing the method I implemented. The output should look like:
0: "0-10" 100: "11-100" 999: "101-1000" 1117: "1001-10000" 9999: "1001-10000" 15201: "10001-100000"; 1620620: "1000001-10000000"
As you can see, the only difference, in order to make it work, is the “dynamic” range. If you take a look at the output, the range goes from 10 to millions.
Any ideas? Any generic implementation to allow dynamic ranges?
UPDATE
Here is the generic method:
const calculateInterval = (counter, minimumCounter = 0, range = 10) => {
if (counter < minimumCounter) {
throw new Error(
"The given counter must be greater or equal than the minimum counter."
);
}
const start = Math.floor((counter - 1) / range) * range + 1;
const end = start + range - 1;
const interval = `${Math.max(start, minimumCounter)}-${end}`;
return interval;
};
//
// MAIN
//
const counters = [0, 100, 999, 1117, 9999, 15201, 1620620];
counters.forEach((totalFollowers) => {
console.log(`${totalFollowers}: ${calculateInterval(totalFollowers)}`);
});
//
// Q: HOW DO I MAKE THE RANGE DYNAMIC (BASED ON THE AMOUNT OF FOLLOWERS)?
//OUTPUT MUST BE:
0: "0-10" 100: "11-100" 999: "101-1000" 1117: "1001-10000" 9999: "1001-10000" 15201: "10001-100000"; 1620620: "1000001-10000000"
Advertisement
Answer
What you’re looking for is called a logarithmic scale. In this case, the interval is not incremented but multiplied by the range in each step.
You can find the beginning of the range by raising r to the floor of the r-base logarithm of n-1, where r is the range and n is the number.
To get the edge cases right though, you need to make some adjustments (add one to the start of the range, add a default for values smaller or equal to the range, etc):
const baseNlog = (base, x) => Math.log(x) / Math.log(base)
const logarithmicInterval = (n, range = 10) => {
if(n <= range)
return `0-${range}`
const start = range ** Math.floor(baseNlog(range, n-1));
const end = start * range;
const interval = `${start + 1}-${end}`;
return interval;
};
//
// MAIN
//
console.log([
0,
1,
10,
11,
100,
999,
1117,
9999,
15201,
1620620
].map(e => `${e}: ${logarithmicInterval(e)}`))