Skip to content

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

License

Notifications You must be signed in to change notification settings

TSavo/proportionate-js

Repository files navigation

The Proportionate Javascript Library

build status coverage report dependencies Status npm version

Codeship Status for TSavo/proportionate-js Code Climate Test Coverage Issue Count Codacy Badge Codacy Badge CircleCI

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"];

//Default range of 0..99
proportionate(weightRanks, 10); // "fly"
proportionate(weightRanks, 25); // "light"
proportionate(weightRanks, 45); // "medium"
proportionate(weightRanks, 65); // "heavy"
proportionate(weightRanks, 85); // "super-heavy"

//Range of 0..500
proportionate(weightRanks, 50, 500); // "fly"
proportionate(weightRanks, 150, 500); // "light"
proportionate(weightRanks, 250, 500); // "medium"
proportionate(weightRanks, 350, 500); // "heavy"
proportionate(weightRanks, 450, 500); // "super-heavy"

//Range of 80..350
proportionate(weightRanks, 90, 80, 350); // "fly"
proportionate(weightRanks, 180, 80, 350); // "light"
proportionate(weightRanks, 240, 80, 350); // "medium"
proportionate(weightRanks, 290, 80, 350); // "heavy"
proportionate(weightRanks, 330, 80, 350); // "super-heavy"

As a prototype on Array:

// This installs .proportionate on Array.prototype:
require("proportionate/arrays"); 
//This also works:
require("proportionate").arrays();


var weightRanks = ["fly", "light", "medium", "heavy", "super-heavy"];

//Default range of 0..99
weightRanks.proportionate(10); // "fly"
weightRanks.proportionate(25); // "light"
weightRanks.proportionate(45); // "medium"
weightRanks.proportionate(65); // "heavy"
weightRanks.proportionate(85); // "super-heavy"

//Range of 0..500
weightRanks.proportionate(50, 500); // "fly"
weightRanks.proportionate(150, 500); // "light"
weightRanks.proportionate(250, 500); // "medium"
weightRanks.proportionate(350, 500); // "heavy"
weightRanks.proportionate(450, 500); // "super-heavy"

//Range of 80..350
weightRanks.proportionate(90, 80, 350); // "fly"
weightRanks.proportionate(180, 80, 350); // "light"
weightRanks.proportionate(240, 80, 350); // "medium"
weightRanks.proportionate(290, 80, 350); // "heavy"
weightRanks.proportionate(330, 80, 350); // "super-heavy"

Clamped variation

As a module:

var proportionate = require("proportionate/clamped");

var tankFullness = ["empty", "almost empty", "half-full", "mostly full", "completely full"];

//Default range of 0..99
tankFullness.proportionate(0); // "empty"
tankFullness.proportionate(1); // "almost empty"
tankFullness.proportionate(45); // "half-full"
tankFullness.proportionate(98); // "mostly full"
tankFullness.proportionate(99); // "completely full"

As a prototype on Array:

// This installs .proportionate on Array.prototype:
require("proportionate/clamped/arrays"); 
//This also works:
require("proportionate/clamped").arrays();

var tankFullness = ["empty", "almost empty", "half-full", "mostly full", "completely full"];

//Default range of 0..99
tankFullness.proportionate(0); // "empty"
tankFullness.proportionate(1); // "almost empty"
tankFullness.proportionate(45); // "half-full"
tankFullness.proportionate(98); // "mostly full"
tankFullness.proportionate(99); // "completely full"

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

About

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

Resources

License

Stars

Watchers

Forks

Packages

No packages published