From 2837aa90e5be969438e0a6c45bd1a6407c23c554 Mon Sep 17 00:00:00 2001 From: Braden MacDonald Date: Sat, 9 Mar 2024 14:57:10 -0800 Subject: [PATCH] feat: Q string parser can handle plusMinus values --- README.md | 10 ++++++++-- q.ts | 8 +++++--- quantity.ts | 8 ++++++++ tests/q.test.ts | 5 +++++ 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 1742ff6..055ed39 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ x.getSI(); // { magnitude: 5, units: "kg/F" } ## Syntactic Sugar If you prefer, there is a much more compact way to initialize `Quantity` instances: using the `Q` template helper. This -is slightly less efficient and less capable, but far more readable and convenient in many cases. +is slightly less efficient, but far more readable and convenient in many cases. ```ts import { Q } from "@bradenmacdonald/quantity-math-js"; @@ -91,6 +91,12 @@ force.getSI(); // { magnitude: 34.2, units: "N" } force.multiply(Q`2 s^2`).toString(); // "68.4 kg⋅m" ``` +You can also call it as a function, which acts like "parse quantity string": + +```ts +const force = Q("34.2 kg m/s^2"); // new Quantity(34.2, {units: "kg m/s^2"}) +``` + ## Error/uncertainty/tolerance You can specify a "plus/minus" value (in the same units). Operations like addition and multiplication will preserve the @@ -99,7 +105,7 @@ relative uncertainty, etc.). ```ts const x = new Quantity(4.52, { units: "cm", plusMinus: 0.02 }); // 4.52±0.02 cm -const y = new Quantity(2.0, { units: "cm", plusMinus: 0.2 }); // 2±0.2 cm" +const y = Q`2±0.2 cm`; // Or use the Q string syntax const z = x.multiply(y); // z = xy = 9.04 ± 0.944 cm² z.get(); // { magnitude: 9.04, units: "cm^2", plusMinus: 0.944 } z.toString(); // "9.0±0.9 cm^2" (toString() will automatically round the output) diff --git a/q.ts b/q.ts index d108593..2d82889 100644 --- a/q.ts +++ b/q.ts @@ -11,9 +11,11 @@ export function Q(strings: string | ReadonlyArray, ...keys: unknown[]): fullString += strings[i + 1]; } } - const match = /([-+]?\d*\.?\d*)\s*(.*)/.exec(fullString); + const match = /([-+]?\d*\.?\d*)(\s*±\s*([\d\.]+))?\s*(.*)/.exec(fullString); if (match === null) throw new QuantityError(`Unable to parse Q template string: ${fullString}`); const magnitude = parseFloat(match[1]); - const units = match[2]; - return new Quantity(magnitude, { units }); + const plusMinusStr = match[3]; + const plusMinus = plusMinusStr ? parseFloat(plusMinusStr) : undefined; + const units = match[4]; + return new Quantity(magnitude, { units, plusMinus }); } diff --git a/quantity.ts b/quantity.ts index fa9931c..9493734 100644 --- a/quantity.ts +++ b/quantity.ts @@ -176,6 +176,14 @@ export class Quantity { * * Uses the plusMinus tolerance of this quantity value, or if no tolerance * is set, it defaults to a relative tolerance of a hundredth of a percent. + * + * ```ts + * Q`60±5 W`.equalsApprox(Q`63 W`) // true + * Q`60±0.05 W`.equalsApprox(Q`60.1 W`) // false + * // Default tolerance of 0.01% when no ± plusMinus tolerance is specified: + * Q`100 W`.equalsApprox(Q`100.009 W`) // true + * Q`100 W`.equalsApprox(Q`100.02 W`) // false + * ``` */ public equalsApprox(other: Quantity): boolean { if (!this.sameDimensionsAs(other)) return false; diff --git a/tests/q.test.ts b/tests/q.test.ts index 69b75aa..8828bc6 100644 --- a/tests/q.test.ts +++ b/tests/q.test.ts @@ -65,4 +65,9 @@ Deno.test("Constructing Quantity instances with Q`...` template", async (t) => { ) { await check(short, new Quantity(-.05123, { units: "kg⋅m/s^2" })); } + + await check(`15.5 ± 0.2 kg`, new Quantity(15.5, { units: "kg", plusMinus: 0.2 })); + await check(`0.2±.01 g`, new Quantity(0.2, { units: "g", plusMinus: 0.01 })); + await check(`60±5 W`, new Quantity(60, { units: "W", plusMinus: 5 })); + await check(`+60±5.0000 W`, new Quantity(60, { units: "W", plusMinus: 5 })); });