mon-entreprise/source/engine/units.js

80 lines
2.0 KiB
JavaScript

import { remove, isEmpty, unnest } from 'ramda'
//TODO this function does not handle complex units like passenger-kilometer/flight
export let parseUnit = string => {
let [a, b = ''] = string.split('/'),
result = {
numerators: a !== '' ? [a] : [],
denominators: b !== '' ? [b] : []
}
return result
}
let printUnits = units => units.filter(unit => unit !== '%').join('-')
export let serialiseUnit = rawUnit => {
let unit = simplify(rawUnit),
{ numerators = [], denominators = [] } = unit
// the unit '%' is only displayed when it is the only unit
let merge = [...numerators, ...denominators]
if (merge.length === 1 && merge[0] === '%') return '%'
let n = !isEmpty(numerators)
let d = !isEmpty(denominators)
let string =
!n && !d
? ''
: n && !d
? printUnits(numerators)
: !n && d
? `/${printUnits(denominators)}`
: `${printUnits(numerators)} / ${printUnits(denominators)}`
return string
}
let noUnit = { numerators: [], denominators: [] }
export let inferUnit = (operator, rawUnits) => {
let units = rawUnits.map(u => u || noUnit)
if (operator === '*')
return simplify({
numerators: unnest(units.map(u => u.numerators)),
denominators: unnest(units.map(u => u.denominators))
})
if (operator === '/') {
if (units.length !== 2)
throw new Error('Infer units of a division with units.length !== 2)')
return inferUnit('*', [
units[0],
{
numerators: units[1].denominators,
denominators: units[1].numerators
}
])
}
if (operator === '-' || operator === '+') {
return rawUnits.find(u => u)
}
return null
}
export let removeOnce = element => list => {
let index = list.indexOf(element)
if (index > -1) return remove(index, 1)(list)
else return list
}
let simplify = unit =>
[...unit.numerators, ...unit.denominators].reduce(
({ numerators, denominators }, next) =>
numerators.includes(next) && denominators.includes(next)
? {
numerators: removeOnce(next)(numerators),
denominators: removeOnce(next)(denominators)
}
: { numerators, denominators },
unit
)