Skip to content

Instantly share code, notes, and snippets.

@well1791
Created May 28, 2023 23:39
Show Gist options
  • Save well1791/bcfc94972575ac9a175acc45be7abf6b to your computer and use it in GitHub Desktop.
Save well1791/bcfc94972575ac9a175acc45be7abf6b to your computer and use it in GitHub Desktop.
measures
import type * as t from './types.d'
const ONE_IN_TO_CM = 2.54
const metricMap: t.UnitMap<t.MetricUnit> = {
mapList: ['km', 'hm', 'dam', 'm', 'dm', 'cm', 'mm'],
updater: {
inc: (a) => a * 10,
dec: (a) => a / 10,
}
}
const imperialMap: t.UnitMap<t.ImperialUnit> = {
mapList: ['mi', 1760, 'yd', 3, 'ft', 12, 'in'],
updater: {
inc: (a, b) => isNaN(Number(b)) ? a : a * Number(b),
dec: (a, b) => isNaN(Number(b)) ? a : a / Number(b),
}
}
const isMetric = (a: t.LengthUnit): a is t.MetricUnit =>
metricMap.mapList.includes(a as t.MetricUnit)
const isImperial = (a: t.LengthUnit): a is t.MetricUnit =>
imperialMap.mapList.includes(a as t.ImperialUnit)
const getUnitDist = <T extends t.LengthUnit> (
unitMap: t.UnitMap<T>,
fromUnit: T,
toUnit: T,
): number => {
if (fromUnit === toUnit)
return 1
const [fIndex, tIndex] = [fromUnit, toUnit].map((a) => unitMap.mapList.indexOf(a))
const [start, end] = [Math.min, Math.max].map((fn) => fn(fIndex, tIndex) + 1)
const op = fIndex > tIndex ? unitMap.updater.dec : unitMap.updater.inc
return unitMap.mapList.slice(start, end).reduce<number>(op, 1)
}
const dunno = (
[value, measure]: t.Length,
toMeasure: t.LengthUnit,
): t.Length => {
const conds: [() => number, () => boolean][] = [
[
() => value * getUnitDist<t.MetricUnit>(
metricMap,
measure as t.MetricUnit,
toMeasure as t.MetricUnit,
),
() => isMetric(measure) && isMetric(toMeasure),
],
[
() => value * getUnitDist<t.ImperialUnit>(
imperialMap,
measure as t.ImperialUnit,
toMeasure as t.ImperialUnit,
),
() => isImperial(measure) && isImperial(toMeasure),
],
[
() => value
* getUnitDist<t.ImperialUnit>(imperialMap, measure as t.ImperialUnit, 'in')
* getUnitDist<t.MetricUnit>(metricMap, 'cm', toMeasure as t.MetricUnit)
* ONE_IN_TO_CM,
() => isMetric(toMeasure)
],
[
() => (
value
* getUnitDist<t.MetricUnit>(metricMap, measure as t.MetricUnit, 'cm')
* getUnitDist<t.ImperialUnit>(imperialMap, 'in', toMeasure as t.ImperialUnit)
) / ONE_IN_TO_CM,
() => isImperial(toMeasure)
],
]
const [getResult] = conds.find(([, test]) => test()) || [() => value]
return [getResult(), toMeasure]
}
const test = (result: t.Length, expect: string) => {
const r = result.join('')
console.log(r === expect, ` -> received(${r}) === expected(${expect})`)
console.log('------------------------------')
}
test(dunno([2, 'km'], 'm'), '2000m')
test(dunno([2, 'mm'], 'm'), '0.002m')
test(dunno([1, 'yd'], 'in'), '36in')
test(dunno([3, 'in'], 'ft'), '0.25ft')
test(dunno([1, 'yd'], 'm'), '0.9144m')
test(dunno([2.54, 'cm'], 'in'), '1in')
test(dunno([91.44, 'mm'], 'in'), '3.6in')
export type MetricUnits = ['km', 'hm', 'dam', 'm', 'dm', 'cm', 'mm']
export type ImperialUnits = ['mi', 'yd', 'ft', 'in']
export type LengthUnits = MetricUnits | ImperialUnits
export type MetricUnit = MetricUnits[number]
export type ImperialUnit = ImperialUnits[number]
export type LengthUnit = ImperialUnit | MetricUnit
export type Length = [number, LengthUnit]
export type Operation<T> = (value: number, measureUnit: T) => number
export type UnitMap<T extends LengthUnit, P extends T | number = T | number> = {
mapList: P[]
updater: { inc: Operation<P>, dec: Operation<P>}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment