feat: add rate normalization helpers

This commit is contained in:
admin123 2026-04-20 20:01:43 +08:00
parent b6b350bbf7
commit 065b6380bf
2 changed files with 117 additions and 0 deletions

View File

@ -0,0 +1,88 @@
type ComparableRate = {
isLessThan: boolean;
numeric: number;
};
export function normalizeRateDisplay(value: string): string {
const trimmedValue = value.trim();
const rangeMatch = trimmedValue.match(
/^([0-9]+(?:\.[0-9]+)?)\s*%?\s*-\s*([0-9]+(?:\.[0-9]+)?)\s*%$/
);
if (rangeMatch) {
const [, lowerBound, upperBound] = rangeMatch;
return `${lowerBound}% - ${upperBound}%`;
}
return trimmedValue.replace(/\s+/g, "");
}
export function parseRateLowerBound(value: string | null | undefined): number | null {
const comparableRate = toComparableRate(value);
return comparableRate?.numeric ?? null;
}
export function compareRateValues(
leftValue: string | null | undefined,
rightValue: string | null | undefined
): number {
const leftComparable = toComparableRate(leftValue);
const rightComparable = toComparableRate(rightValue);
if (!leftComparable && !rightComparable) {
return 0;
}
if (!leftComparable) {
return 1;
}
if (!rightComparable) {
return -1;
}
if (leftComparable.numeric !== rightComparable.numeric) {
return leftComparable.numeric - rightComparable.numeric;
}
if (leftComparable.isLessThan === rightComparable.isLessThan) {
return 0;
}
return leftComparable.isLessThan ? -1 : 1;
}
function toComparableRate(value: string | null | undefined): ComparableRate | null {
if (!value) {
return null;
}
const normalizedValue = normalizeRateDisplay(value);
const lessThanMatch = normalizedValue.match(/^<\s*([0-9]+(?:\.[0-9]+)?)%$/);
if (lessThanMatch) {
return {
isLessThan: true,
numeric: Number(lessThanMatch[1])
};
}
const rangeMatch = normalizedValue.match(
/^([0-9]+(?:\.[0-9]+)?)%\s*-\s*([0-9]+(?:\.[0-9]+)?)%$/
);
if (rangeMatch) {
return {
isLessThan: false,
numeric: Number(rangeMatch[1])
};
}
const exactMatch = normalizedValue.match(/^([0-9]+(?:\.[0-9]+)?)%$/);
if (exactMatch) {
return {
isLessThan: false,
numeric: Number(exactMatch[1])
};
}
return null;
}

View File

@ -0,0 +1,29 @@
import { describe, expect, test } from "vitest";
import {
compareRateValues,
normalizeRateDisplay,
parseRateLowerBound
} from "../src/shared/rate-normalizer";
describe("rate-normalizer", () => {
test("normalizes compact ranges", () => {
expect(normalizeRateDisplay("0.5%-1%")).toBe("0.5% - 1%");
});
test("normalizes ranges with missing percent on the lower bound", () => {
expect(normalizeRateDisplay("0.02 - 0.1%")).toBe("0.02% - 0.1%");
});
test("parses the lower bound of a range", () => {
expect(parseRateLowerBound("0.02% - 0.1%")).toBe(0.02);
});
test("treats less-than values as smaller than the boundary range", () => {
expect(compareRateValues("<0.02%", "0.02% - 0.1%")).toBeLessThan(0);
});
test("orders missing values after real values", () => {
expect(compareRateValues(null, "0.02% - 0.1%")).toBeGreaterThan(0);
});
});