refactor(api): extract rate card match support
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
type MatchKey = "roleId" | "chapter" | "location" | "seniority" | "workType";
|
||||
|
||||
type MatchableRateCardLine = Partial<Record<MatchKey, string | null>>;
|
||||
type RateCardLineMatchCriteria = Partial<Record<MatchKey, string | undefined>>;
|
||||
type RateCardLineMatchWeights = Partial<Record<MatchKey, number>>;
|
||||
|
||||
type RateCardLineMatchResult<TLine> = {
|
||||
line: TLine;
|
||||
score: number;
|
||||
mismatch: boolean;
|
||||
};
|
||||
|
||||
function normalizeRateCardMatchValue(
|
||||
value: string,
|
||||
caseInsensitive: boolean,
|
||||
): string {
|
||||
return caseInsensitive ? value.toLowerCase() : value;
|
||||
}
|
||||
|
||||
function rateCardMatchValuesEqual(input: {
|
||||
lineValue: string;
|
||||
criteriaValue: string;
|
||||
caseInsensitive: boolean;
|
||||
}): boolean {
|
||||
return normalizeRateCardMatchValue(input.lineValue, input.caseInsensitive) ===
|
||||
normalizeRateCardMatchValue(input.criteriaValue, input.caseInsensitive);
|
||||
}
|
||||
|
||||
export function scoreRateCardLineMatch<TLine extends MatchableRateCardLine>(
|
||||
line: TLine,
|
||||
input: {
|
||||
criteria: RateCardLineMatchCriteria;
|
||||
weights: RateCardLineMatchWeights;
|
||||
caseInsensitiveKeys?: readonly MatchKey[];
|
||||
},
|
||||
): RateCardLineMatchResult<TLine> {
|
||||
const caseInsensitiveKeys = new Set(input.caseInsensitiveKeys ?? []);
|
||||
let score = 0;
|
||||
let mismatch = false;
|
||||
|
||||
for (const [key, criteriaValue] of Object.entries(input.criteria) as Array<[MatchKey, string | undefined]>) {
|
||||
const lineValue = line[key];
|
||||
if (!criteriaValue || !lineValue) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (rateCardMatchValuesEqual({
|
||||
lineValue,
|
||||
criteriaValue,
|
||||
caseInsensitive: caseInsensitiveKeys.has(key),
|
||||
})) {
|
||||
score += input.weights[key] ?? 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
mismatch = true;
|
||||
}
|
||||
|
||||
return { line, score, mismatch };
|
||||
}
|
||||
|
||||
export function pickBestRateCardLineMatch<
|
||||
TLine extends MatchableRateCardLine,
|
||||
>(
|
||||
lines: TLine[],
|
||||
input: {
|
||||
criteria: RateCardLineMatchCriteria;
|
||||
weights: RateCardLineMatchWeights;
|
||||
caseInsensitiveKeys?: readonly MatchKey[];
|
||||
},
|
||||
): TLine | null {
|
||||
const candidates = lines
|
||||
.map((line) => scoreRateCardLineMatch(line, input))
|
||||
.filter((candidate) => !candidate.mismatch)
|
||||
.sort((left, right) => right.score - left.score);
|
||||
|
||||
return candidates[0]?.line ?? null;
|
||||
}
|
||||
Reference in New Issue
Block a user