Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Annotate #9

Merged
merged 3 commits into from
Jul 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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