munch-ease-frontend/src/units.ts
jableader 211489ca55
Some checks failed
CI / build-test (push) Has been cancelled
pruning (#3)
Reviewed-on: #3
Co-authored-by: jableader <jacobdunk@gmail.com>
Co-committed-by: jableader <jacobdunk@gmail.com>
2025-10-25 02:18:30 +00:00

139 lines
3.3 KiB
TypeScript

const UNIT_KEYS_ARRAY: readonly ['kg', 'litres', 'items'] = ['kg', 'litres', 'items']
type UnitKey = typeof UNIT_KEYS_ARRAY[number]
const equivalentUnits: Record<UnitKey, Record<string, number>> = {
kg: {
kgs: 1,
kilograms: 1,
kilogram: 1,
g: 1000,
gram: 1000,
grams: 1000,
lb: 2.20462,
lbs: 2.20462,
pound: 2.20462,
pounds: 2.20462,
},
litres: {
l: 1,
liter: 1,
litre: 1,
ml: 1000,
milliliters: 1000,
milliliter: 1000,
'fl oz': 33.814,
'fluid ounce': 33.814,
'fluid ounces': 33.814,
cup: 4.22675,
cups: 4.22675,
tbsp: 67.628,
tablespoon: 67.628,
tablespoons: 67.628,
tsp: 202.884,
teaspoon: 202.884,
teaspoons: 202.884,
pt: 2.11338,
pint: 2.11338,
pints: 2.11338,
qt: 1.05669,
quart: 1.05669,
quarts: 1.05669,
gal: 0.264172,
gallon: 0.264172,
gallons: 0.264172,
oz: 35.1951,
ounce: 35.1951,
},
items: {
item: 1,
items: 1,
pcs: 1,
piece: 1,
pieces: 1,
florets: 8, // Broccoli
head: 1, // Broccoli
heads: 1, // Broccoli
slice: 10, // Bread
slices: 10, // Bread
loaf: 1, // Bread
loaves: 1, // Bread
cloves: 8, // Garlic
bulb: 1, // Garlic
bulbs: 1, // Garlic
stalk: 1, // Celery
stalks: 1, // Celery
bunch: 1, // Cilantro
bunches: 1, // Cilantro
sprig: 1, // Cilantro
sprigs: 1, // Cilantro
cans: 1, // Canned goods
can: 1, // Canned goods
pack: 1, // Packaged goods
packs: 1, // Packaged goods
package: 1, // Packaged goods
packages: 1, // Packaged goods
container: 1, // Packaged goods
containers: 1, // Packaged goods
},
}
const UNIT_KEYS_SET: ReadonlySet<string> = new Set(UNIT_KEYS_ARRAY)
function isUnitKey(value: string): value is UnitKey {
return UNIT_KEYS_SET.has(value)
}
function getBaseUnit(unit: string): UnitKey | null {
for (const type of UNIT_KEYS_ARRAY) {
const group = equivalentUnits[type]
if (unit in group) return type
}
return null
}
export function getConversionFactor(unit: string): { unit: UnitKey | string; factor: number } | null {
if (isUnitKey(unit)) return { unit, factor: 1 }
const unitLower = String(unit).toLowerCase()
if (isUnitKey(unitLower)) return { unit: unitLower, factor: 1 }
const baseUnit = getBaseUnit(unit)
if (baseUnit) {
const factor = equivalentUnits[baseUnit]?.[unit]
if (typeof factor === 'number') return { unit: baseUnit, factor }
return null
}
const baseUnitLower = getBaseUnit(unitLower)
if (baseUnitLower) {
const factor = equivalentUnits[baseUnitLower]?.[unitLower]
if (typeof factor === 'number') return { unit: baseUnitLower, factor }
return null
}
return null
}
type Quantity = { quantity: number; unit: string }
type Total = { unit: string; quantity: number }
export function calculateTotals(quantityList: Quantity[]): Total[] {
const totals: Record<string, number> = {}
for (const quantity of quantityList) {
const baseUnit = getConversionFactor(quantity.unit) ?? { unit: quantity.unit, factor: 1 }
const factor = baseUnit.factor
const unit = String(baseUnit.unit)
totals[unit] = (totals[unit] ?? 0) + quantity.quantity / factor
}
return Object.keys(totals).map((unit) => ({ unit, quantity: totals[unit] ?? 0 }))
}