feat: add rate normalization helpers
This commit is contained in:
parent
b6b350bbf7
commit
065b6380bf
88
src/shared/rate-normalizer.ts
Normal file
88
src/shared/rate-normalizer.ts
Normal 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;
|
||||
}
|
||||
29
tests/rate-normalizer.test.ts
Normal file
29
tests/rate-normalizer.test.ts
Normal 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);
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user