const UNIT_KEYS_ARRAY: readonly ['kg', 'litres', 'items'] = ['kg', 'litres', 'items'] type UnitKey = typeof UNIT_KEYS_ARRAY[number] const equivalentUnits: Record> = { 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 = 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 = {} 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 })) }