Skip to content

Commit

Permalink
Annotate (#9)
Browse files Browse the repository at this point in the history
* annotated unit keys, fixes #7

* annotated Measure and measure.to

* annotated measureFrom
  • Loading branch information
varna authored Jul 25, 2019
1 parent ea3a0e7 commit 72d11de
Show file tree
Hide file tree
Showing 13 changed files with 679 additions and 447 deletions.
73 changes: 73 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,79 @@ measure(10000) // 10_000 || "1 ha"
measure(1000000) // 1_000_000 || "1 km²"
```

## Units

### SI prefixes

Meters (normal, square and cubic), grams and litres are prefixable with [SI prefixes](https://en.wikipedia.org/wiki/Metric_prefix#List_of_SI_prefixes). Examples:

| Key | Unit | Value |
| --- | ---------------- | --------------- |
| km | kilometre | 1 \* 1000 |
| cm | centimetre | 1 / 100 |
| ml | mililitre | 1 / 1000 |
| km2 | square kilometre | 1 _ 1000 _ 1000 |

### Length

| Key | Name | Value |
| --- | ------------- | --------------- |
| m | Meter (meter) | 1 |
| M | Nautical Mile | Metre \* 1852 |
| in | Inch | Foot / 12 |
| ft | Foot | Yard / 3 |
| yd | Yard | Metre \* 0.9144 |
| ch | Chain | Yard \* 22 |
| fur | Furlong | Chain \* 10 |
| mi | Mile | Furlong \* 8 |
| lea | League | Mile \* 3 |

### Area

Square length units can be used to measure area, i.e. "m2".

| Key | Name | Value |
| --- | ------- | ----------------- |
| a | Are | Metre \*_ 2 _ 100 |
| ha | Hectare | Are \* 100 |
| ac | Acre | Yard \*_ 2 _ 4840 |

### Volume

Cubic length units can be used to measure volume, i.e. "m3".

I'm not sure how imperial volume units "really" work (in practise), so create an issue or PR to fix anything that is a miss.

| Key | Name | Value |
| ----- | ---------------------- | -------------------------- |
| l | Litre | Metre \*\* 3 / 1000 |
| pint | Pint (Imperial) | Litre \* 0.568 |
| gal | Gallon (American) | Inch \*\* 3 / 231 |
| qt | Quart (American) | Gallon / 4 |
| pt | Pint (American) | Gallon / 8 |
| gi | Gill (American) | Gallon / 32 |
| fl oz | Fluid Ounce (American) | Gallon / 128 |
| peck | Peck | Litre \* 8.809_768 |
| bu | Bushel | Litre \* 35.239_070_166_88 |

### Mass (weight)

| Key | Name | Value |
| ------- | ------------------------------ | ------------------ |
| kg | Kilogram | 1 |
| g | Gram | Kilogram / 1000 |
| lb | Pound | Gram \* 453.592_37 |
| t | Tonne (Metric Ton) | Kilogram \* 1000 |
| ton | American Ton (short) | Pound \* 2000 |
| longton | Imperial Ton (long) | Pound \* 2240 |
| cwt | American Hundredweight (short) | Pound \* 100 |
| longcwt | Imperial Hundredweight (long) | Pound \* 112 |
| qr | Quarter | Pound \* 28 |
| st | Stone | Pound \* 14 |
| oz | Ounce | Pound / 16 |
| dr | Drachm | Pound / 256 |
| gr | Grain | Pound / 7000 |

## Roadmap

- [Suggest units](https://stackoverflow.com/questions/56947641/generating-union-string-type)
Expand Down
28 changes: 14 additions & 14 deletions __tests__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,10 +262,10 @@ describe('Volume units', () => {
expect(String(res)).toBe('1 pt')
})

test('pt', () => {
test('pint', () => {
const measure = new Measure(0.000568261)
const res = measure.to('pint_long')
expect(String(res)).toBe('1 pt')
const res = measure.to('pint')
expect(String(res)).toBe('1 pint')
})

test('fl oz', () => {
Expand Down Expand Up @@ -301,16 +301,16 @@ describe('Weight units', () => {
const res = measure.to('t')
expect(String(res)).toBe('1 t')
})
test('t US', () => {
test('ton', () => {
const measure = new Measure(907.185)
const res = measure.to('ton_short')
expect(String(res)).toBe('1 t')
const res = measure.to('ton')
expect(String(res)).toBe('1 ton')
})

test('t UK', () => {
test('longton', () => {
const measure = new Measure(1016.05)
const res = measure.to('ton_long')
expect(String(res)).toBe('1 t')
const res = measure.to('longton')
expect(String(res)).toBe('1 longton')
})

test('lb', () => {
Expand Down Expand Up @@ -349,15 +349,15 @@ describe('Weight units', () => {
expect(String(res)).toBe('1 qr')
})

test('cwt UK', () => {
test('longcwt', () => {
const measure = new Measure(50.8)
const res = measure.to('hundredweight_long')
expect(String(res)).toBe('1 cwt')
const res = measure.to('longcwt')
expect(String(res)).toBe('1 longcwt')
})

test('cwt US', () => {
test('cwt', () => {
const measure = new Measure(45.36)
const res = measure.to('hundredweight_short')
const res = measure.to('cwt')
expect(String(res)).toBe('1 cwt')
})
})
4 changes: 2 additions & 2 deletions src/findUnit.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import units from './units'
import units, { UnitKey } from './units'
import siPrefixes from './SIPrefixes'

const SQUARE_SYMBOLS = ['²', '2']
const CUBE_SYMBOLS = ['³', '3']

const findUnit = (
unitString: string
unitString: UnitKey
): {
unit: string
prefix: string
Expand Down
39 changes: 28 additions & 11 deletions src/measure.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
// import siPrefixes from './SIPrefixes'
// import units, { LENGTH } from './units'
import findUnit from './findUnit'
import { UnitKey } from './units'

/**
* Measure space in meters.
* Measure of value with unit.
* Length, area, volume and mass units.
* SI, metric and imperial units.
* Squared and cubed length units (m2, m3, km2, m3, ft3, mi2).
* SI prefixes (da, h, k, M, G, T, P, E, Z, Y, d, c, m, μ, n, p, f, a, z, y) for (m, m2, m3, l, g) i.e. (km2, kg, cm, ml).
*
* @returns {Number|String} `Measure.valueOf(): Number` or `Measure.toString(): String` depending on context
*/
export default class Measure extends Number implements Number {
public precision = 12
Expand All @@ -14,22 +19,28 @@ export default class Measure extends Number implements Number {
public suffix = ''
public round = 2

constructor(value: number | string | Measure, unitString?: string) {
/**
* @param value value/quantity of unit
* @param unitKey key representation of unit
*/
constructor(value: number | string | Measure, unitKey?: UnitKey) {
super(value)
if (unitString) {
const { unit, prefix, suffix, ratio } = findUnit(unitString.trim())
if (unitKey) {
const { unit, prefix, suffix, ratio } = findUnit(unitKey)
this.symbol = unit
this.prefix = prefix
this.suffix = suffix
this.ratio = 1 / ratio
}
}

public to = (unitString: string): Measure => {
// check if squared or cubed
let trimmed = unitString.trim()

const { unit, prefix, suffix, ratio } = findUnit(trimmed)
/**
* Convert Measure to another Measure with different Unit.
*
* @param unitKey key representation of unit
*/
public to = (unitKey: UnitKey): Measure => {
const { unit, prefix, suffix, ratio } = findUnit(unitKey)
this.symbol = unit
this.prefix = prefix
this.suffix = suffix
Expand All @@ -41,6 +52,12 @@ export default class Measure extends Number implements Number {
return this.clone(newValue)
}

/**
* Create a copy of of Measure with new value.
* Why? Because value of Number that we extend is immutable.
*
* @param value new value
*/
public clone(value: number) {
const measure = new Measure(value)
measure.ratio = this.ratio
Expand Down
26 changes: 20 additions & 6 deletions src/measureSelector.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import findUnit from './findUnit'
import Measure from './measure'
import { UnitKey } from './units'

const toMeasure = (
{
Expand All @@ -14,18 +15,29 @@ const toMeasure = (
value: number
) => {
const unitString = prefix + unit + suffix
return new Measure(value).to(unitString)
return new Measure(value).to(<UnitKey>unitString)
}

const createMeasureSelector = (...args: string[]) => {
const units = args.map(findUnit).sort((a, b) => b.ratio - a.ratio)
/**
* Creates a function that finds best looking unit for value from a list of provided units.
* @param unitKeys an Array of unit identifiers.
* @returns {(value: number) => Measure} Finds best looking unit for pretty printing and returns Measure with it.
*/
const createMeasureSelector = (...unitKeys: UnitKey[]) => {
const units = unitKeys.map(findUnit).sort((a, b) => b.ratio - a.ratio)

return function(value: number) {
/**
* Finds best looking unit for pretty printing and returns Measure with it.
*
* @param value quantity/value of measure.
* @returns {Measure} new Measure(value, bestLookingUnit)
*/
const finder = (value: number) => {
const found = units.reduce(
// find smallest unit larger than 1
(previousUnit, currentUnit) => {
const currentQuantity = Number(toMeasure(currentUnit, value)) //currentUnit.quantity(value)
const previousQuantity = Number(toMeasure(previousUnit, value)) // previousUnit.quantity(value)
const currentQuantity = Number(toMeasure(currentUnit, value))
const previousQuantity = Number(toMeasure(previousUnit, value))

return previousQuantity < 1 || currentQuantity < previousQuantity
? currentUnit
Expand All @@ -41,6 +53,8 @@ const createMeasureSelector = (...args: string[]) => {
return foundMeasure
}
}

return finder
}

export default createMeasureSelector
33 changes: 0 additions & 33 deletions src/unitKeys.ts

This file was deleted.

Loading

0 comments on commit 72d11de

Please sign in to comment.