The Proportionate Javascript Library

What it does

Convenience methods for dealing with proportions (a part, share, or number considered in comparative relation to a whole).

Specifically it allows you to select something from an array indexed proportionate to a number within an arbitrary range.

Useful when you need to select from a small list of things given a broad range of options.

How it does it

It takes the form:

proportionate(sampleArray, valueInRange, [rangeMax(orMin), [rangeMax]]) => sample

It uses the formula:

index = max(sampleSize, min(0, sampleSize * round((part - rangeMin) / (rangeMax - rangeMin)) - 1))

SampleArray and valueInRange are required arguments. SampleArray must be an array. RangeMin and rangeMax default to 0 and 99, respectively. If you specify one range argument, it's 0..rangeMax, but if you specify both, it's rangeMin..rangeMax.

Expected output

Given the sampleArray: [1, 2, 3]

And the (default) range: 0, 99

Or in functional notation:

[0..99].map(x -> proportionate([1, 2, 3], x))

The expected truth table would be:

Input Output 0-32 1 33-65 2 66-99 3

Variation: Clamped Proportionate

What it does

Clamped proportionate has the exact same interface, but only returns the extreme values when the input is equal to the extreme of the range.

How it does it

It uses the formula:

index = actual <= 0 ? 0 : actual >= max ? sampleSize - 1 : max(sampleSize, min(1, sampleSize * round((valueInRange - rangeMin) / (rangeMax - rangeMin)) - 2))

Expected output

Given the sampleArray: [1, 2, 3]

And the (default) range: 0, 99

Or in functional notation:

[0..99].map(x -> proportionate([1, 2, 3], x))

The expected truth table would be:

Input Output 0 1 1-98 2 99 3

Why do I need it?

It's shorthand for the following rather unreadable code, seen many times in the wild:

array[Math.max(array.length, Math.min(0, array.length * Math.round((actual - min) / (max - min)) - 1))]

It replaces it with:

proportionate(array, actual, min, max)

Or, assuming a min of 0:

proportionate(array, actual, max)

Or, assuming a range of 0..99:

proportionate(array, actual)

Or, when using the Array.prototype option:

array.proportionate(actual, min, max) array.proportionate(actual, max) array.proportionate(actual)

Installation

npm install --save proportionate

Example Usage

As a module:

var proportionate = require ( " proportionate " ) ; var weightRanks = [ " fly " , " light " , " medium " , " heavy " , " super-heavy " ] ; proportionate ( weightRanks , 10 ) ; proportionate ( weightRanks , 25 ) ; proportionate ( weightRanks , 45 ) ; proportionate ( weightRanks , 65 ) ; proportionate ( weightRanks , 85 ) ; proportionate ( weightRanks , 50 , 500 ) ; proportionate ( weightRanks , 150 , 500 ) ; proportionate ( weightRanks , 250 , 500 ) ; proportionate ( weightRanks , 350 , 500 ) ; proportionate ( weightRanks , 450 , 500 ) ; proportionate ( weightRanks , 90 , 80 , 350 ) ; proportionate ( weightRanks , 180 , 80 , 350 ) ; proportionate ( weightRanks , 240 , 80 , 350 ) ; proportionate ( weightRanks , 290 , 80 , 350 ) ; proportionate ( weightRanks , 330 , 80 , 350 ) ;

As a prototype on Array:

require ( " proportionate/arrays " ) ; require ( " proportionate " ) . arrays ( ) ; var weightRanks = [ " fly " , " light " , " medium " , " heavy " , " super-heavy " ] ; weightRanks . proportionate ( 10 ) ; weightRanks . proportionate ( 25 ) ; weightRanks . proportionate ( 45 ) ; weightRanks . proportionate ( 65 ) ; weightRanks . proportionate ( 85 ) ; weightRanks . proportionate ( 50 , 500 ) ; weightRanks . proportionate ( 150 , 500 ) ; weightRanks . proportionate ( 250 , 500 ) ; weightRanks . proportionate ( 350 , 500 ) ; weightRanks . proportionate ( 450 , 500 ) ; weightRanks . proportionate ( 90 , 80 , 350 ) ; weightRanks . proportionate ( 180 , 80 , 350 ) ; weightRanks . proportionate ( 240 , 80 , 350 ) ; weightRanks . proportionate ( 290 , 80 , 350 ) ; weightRanks . proportionate ( 330 , 80 , 350 ) ;

Clamped variation

As a module:

var proportionate = require ( " proportionate/clamped " ) ; var tankFullness = [ " empty " , " almost empty " , " half-full " , " mostly full " , " completely full " ] ; tankFullness . proportionate ( 0 ) ; tankFullness . proportionate ( 1 ) ; tankFullness . proportionate ( 45 ) ; tankFullness . proportionate ( 98 ) ; tankFullness . proportionate ( 99 ) ;

As a prototype on Array:

require ( " proportionate/clamped/arrays " ) ; require ( " proportionate/clamped " ) . arrays ( ) ; var tankFullness = [ " empty " , " almost empty " , " half-full " , " mostly full " , " completely full " ] ; tankFullness . proportionate ( 0 ) ; tankFullness . proportionate ( 1 ) ; tankFullness . proportionate ( 45 ) ; tankFullness . proportionate ( 98 ) ; tankFullness . proportionate ( 99 ) ;

Pull requests are welcome, please file any bugs on https://github.com/tsavo/proportionate-js