mx_code
mx_code

Reputation: 2517

How to map a value from one range to another in javascript?

How to map a value from one range to another in javascript?

For example take numbers from 0 to 256 which is the range of colours.

How can I map this to to a scale of 0 to 7.

For example if I take a number like 125 in the range (0, 256) should corresponds to the value either 3 or 4 in the range (0,7).

This is what I have tried. But it is not working as expected.

const scale = (number, fromRange, toRange) => {
  return (
      ((number - fromRange[0]) * (toRange[1] - toRange[0])) /
          (fromRange[1] - fromRange[0]) + toRange[0]
  );
};

// Usage: scale(125, [0,256], [0,7]) should return
// either 3 or 4 (could be math.ceil or math.floor)

So My main idea is to map a length of a slider to another range of values.

For example if the clientWidth of the slider is 300px (so the range is (0, 300)) then I want to map this length of the slider to a range of values like (0 to 36) or (0, 100) or (0,512) like that.... So if the current position on the slider is 150px (which is the middle of the slider) then it should correspond to value 18 in the range (0, 36) or 50 in (0, 100) or 256 in (0,512) like that....

I hope that's clear.

NOTE It is not just starting from 0, it could start from any number and end in any number ( talking about the range). I hope you got me.

Upvotes: 1

Views: 5427

Answers (3)

nachospiu
nachospiu

Reputation: 2039

A closed interval is an interval which includes all its limit points, so in the interval [0, 300] you have 301 units (300 - 0 + 1) and 150 is not in the exact middle of the interval (the middle of the interval is 150.5).

In scale functions:

  • First I get the units of each interval (count integer numbers in the interval).
  • Then I get number (parameter) position in from interval and the percentage of that position in from interval. The number position is important if the from interval not start in zero.
  • Continue I use the percentage to get the number position in the new interval (I use Math.round to round the value), and then return the number in that position.
const scale = (number, fromInterval, toInterval) => {
         if(number >= fromInterval[0] && number <= fromInterval[1]) {
                let oldIntervalUnits = fromInterval[1] - fromInterval[0] + 1;
                let newIntervalUnits = toInterval[1] - toInterval[0] + 1;
    
                let oldNumberPosition = -fromInterval[0] + number + 1;
    
                let percentage = oldNumberPosition / oldIntervalUnits;
                
                let newNumberPosition = Math.round(percentage * newIntervalUnits);
       
                return toInterval[0] + newNumberPosition - 1;
        }
        
        return NaN;
};

let res1 = scale(3, [2, 4], [0, 5]);  //return 3   
let res2 = scale(150, [0, 300], [10, 20]); //return 15

Upvotes: 0

MrCodingB
MrCodingB

Reputation: 2314

We first need to "convert" the ranges to start at 0:

const range = max - min

Also we need to shift the input number:

const shiftedNumber = number - inMin

Then you should get the "percentage" of the number. So how far on the scale it is:

const p = shiftedNumber / inRange;

Then you multiply that by the new range's maximum value to "stretch" it:

const newValueOnRange = p * outRange;

And then you offset it by the minimum value:

const newValue = newValueOnRange + outMin;

So in the end:

const scale = (number, [inMin, inMax], [outMin, outMax]) => {
    // if you need an integer value use Math.floor or Math.ceil here
    return (number - inMin) / (inMax - inMin) * (outMax - outMin) + outMin;
}

console.log(scale(128, [0, 256], [0, 8]));

console.log(scale(64, [0, 256], [0, 8]));

Upvotes: 2

Nina Scholz
Nina Scholz

Reputation: 386620

For taking a function for using as callback for mapping, you need to use a closure over the ranges and return a function which has a value ad paramter and returns a new value.

To simplify the problem, you need to calculate the slope d and use it as factor with the adjusted value.

const
    scale = (fromRange, toRange) => {
        const d = (toRange[1] - toRange[0]) / (fromRange[1] - fromRange[0]);
        return from =>  (from - fromRange[0]) * d + toRange[0];
    };

// single value
console.log(scale([0, 256], [0, 7])(125));

// map
console.log([0, 128, 256].map(scale([0, 256], [0, 7])));

Upvotes: 2

Related Questions