Skip to content
Advertisement

JS – Calculate an interval for a given number

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)}`))
User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement