Question

Use Case

I am learning functional programming. During this exercise, I wanted to create a function that naturally overflows. As an example, if the range of the data is from 2-12, if the value of the input is 12 and you add 1 to it, it becomes 2. Likewise, if you subtract 1 from 2, it becomes 12. It should be noted that I want to improve my math skills because I haven't been thinking like a mathematician for the past 15 years. So forgive me if this looks fairly trivial.

Implementation

The first implementation was the following:

var confineRange(value, min, max) {
  var newValue = value;

  while (newValue > max) {
    newValue -= max;
  }

  while (newValue < min) {
    newValue = max - (min - newValue);
  }

  return newValue;
}

Fiddle Here

This implementation worked, but seeing a loop, meant that it could be improved. So the second version I attempted was this:

function confineRange(value, min, max) {
  if (value > 0) 
    return (value % max - min)
  if (value < 0) 
    return ((value + max) % max) - min
  if (value == 0)
    return max - min;
}

However, this version looks like it could be simplified even more.

Questions

  1. How could this be encapsulate in a single mathematical function?
  2. Would it be possible to create this function using bit-shifting? If so, how?
  3. Is there a more elegant solution using native javascript functions?
Was it helpful?

Solution

First for simplicity let's see for positive integers only, assuming that the max > min relation of the parameters are met, and value is larger or equal to min:

function confineRange(value, min, max) {
 return ((value - min) % (max - min + 1)) + min;
}

The bottom end of your range is min, and you want to confine within a max - min + 1 range. For example with your example of min = 2 and max = 12 the result can get 11 distinct values! (And not 10 like it may seem first).

Let's test it with the edge cases:

confineRange(2, 2, 12);

This will execute ((2 - 2) % (12 - 2 + 1)) + 2, which results 2, good.

confineRange(12, 2, 12);

This will execute ((12 - 2) % (12 - 2 + 1)) + 2, which results 12, also good. Note that 12 - 2 makes 10, which is the largest integer which will pass through % 11 unmodified. Now let's try with 13:

confineRange(13, 2, 12);

Now after partially calculating it, it will look like (11 % 11) + 2, which will make 2 as expected (that is if that 13 was reached as 12 + 1, then it will seem to properly wrap around to 2).

So the basic idea is this.

The function however might fail with numbers below 2 due to how Javascript may or may not handle negative numbers, for example see this article. I don't know whether the behaviour is standardized or particular browsers may or may not try to fix it, so better assume this undefined.

The problem now is that you have no possibility of feeding negative numbers in the '%' operator, but given this interface you can only solve the problem with '%' (modulo). So an other method has to be employed.

JavaScript has a Math.floor() function which always rounds downwards (check it here). Using this can eliminate the problem of negatives, so let's construct our own modulo from it:

function confineRange(value, min, max) {
 t = value - min;
 d = max - min + 1;
 t = t - (Math.floor(t / d) * d);
 return t + min;
}

The modulo happens on the third (t = t - (Math.floor(t / d) * d);) line, this calculates t % d independent of sign. This solution will work properly for numbers below 2 as well, so you will for example get 12 if you supply 1 as value with your example.

For your second question the problem is solvable with bit manipulation, but only if the range (the number of distinct values you want to get) is a power of 2. This case applying a proper AND (&) mask on the value will do the job.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top