I just made this pipe to support recipe portions. It is only for positive values and not comprehensive for all fractions:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'quantity',
standalone: true,
})
export class QuantityPipe implements PipeTransform {
transform(quantity: number): string | number {
const decimal = quantity % 1;
if (!decimal) return quantity;
const whole = Math.floor(quantity);
if (approxEq(decimal, 1 / 2)) return whole ? `${whole} ½` : '½';
if (approxEq(decimal, 1 / 3)) return whole ? `${whole} ⅓` : '⅓';
if (approxEq(decimal, 2 / 3)) return whole ? `${whole} ⅔` : '⅔';
if (approxEq(decimal, 1 / 4)) return whole ? `${whole} ¼` : '¼';
if (approxEq(decimal, 3 / 4)) return whole ? `${whole} ¾` : '¾';
if (approxEq(decimal, 1 / 5)) return whole ? `${whole} ⅕` : '⅕';
if (approxEq(decimal, 2 / 5)) return whole ? `${whole} ⅖` : '⅖';
if (approxEq(decimal, 3 / 5)) return whole ? `${whole} ⅗` : '⅗';
if (approxEq(decimal, 4 / 5)) return whole ? `${whole} ⅘` : '⅘';
if (approxEq(decimal, 1 / 6)) return whole ? `${whole} ⅙` : '⅙';
if (approxEq(decimal, 5 / 6)) return whole ? `${whole} ⅚` : '⅚';
if (approxEq(decimal, 1 / 8)) return whole ? `${whole} ⅛` : '⅛';
if (approxEq(decimal, 3 / 8)) return whole ? `${whole} ⅜` : '⅜';
if (approxEq(decimal, 5 / 8)) return whole ? `${whole} ⅝` : '⅝';
if (approxEq(decimal, 7 / 8)) return whole ? `${whole} ⅞` : '⅞';
return Number(quantity.toFixed(2));
}
}
const approxEq = (v1: number, v2: number, epsilon: number = 0.005): boolean =>
Math.abs(v1 - v2) < epsilon;