fix: default to first disposition when text lists two, e.g. "X (Y)"
Some sources list two dispositions together (a team template noting a secondary/alternate, e.g. "Purge the Foe (Reconnaissance)"). Split on the separators that join them and match segment-by-segment left to right, so the first one listed wins instead of whichever scored closer across the combined string. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+19
-5
@@ -143,9 +143,9 @@ const normalizeDispositionText = s => String(s || "").trim().toLowerCase().repla
|
|||||||
// drop "and"/"the" (e.g. "Take Hold") still line up: "Take and Hold" -> "take hold".
|
// drop "and"/"the" (e.g. "Take Hold") still line up: "Take and Hold" -> "take hold".
|
||||||
const dispositionCore = canonical => normalizeDispositionText(canonical).replace(/\b(and|the)\b/g, "").replace(/\s+/g, " ").trim();
|
const dispositionCore = canonical => normalizeDispositionText(canonical).replace(/\b(and|the)\b/g, "").replace(/\s+/g, " ").trim();
|
||||||
|
|
||||||
// Nearest canonical disposition, or null if nothing is close enough to be
|
// Nearest canonical disposition for a single (already-normalized) segment, or
|
||||||
// confident it's a typo/abbreviation rather than unrelated text.
|
// null if nothing is close enough to be confident it's a typo/abbreviation
|
||||||
// Two ways to match:
|
// rather than unrelated text. Two ways to match:
|
||||||
// - Prefix: shorthand like "Recon" or "Disrupt" that's a truncation, not a
|
// - Prefix: shorthand like "Recon" or "Disrupt" that's a truncation, not a
|
||||||
// typo, so Levenshtein distance alone would score it as far away. Gated
|
// typo, so Levenshtein distance alone would score it as far away. Gated
|
||||||
// to 3+ chars so short unrelated words can't collide (the five names'
|
// to 3+ chars so short unrelated words can't collide (the five names'
|
||||||
@@ -153,8 +153,7 @@ const dispositionCore = canonical => normalizeDispositionText(canonical).replace
|
|||||||
// - Levenshtein: everything else, e.g. "Priority Assests". Threshold scales
|
// - Levenshtein: everything else, e.g. "Priority Assests". Threshold scales
|
||||||
// with name length (~25%) so a short unrelated line doesn't accidentally
|
// with name length (~25%) so a short unrelated line doesn't accidentally
|
||||||
// match "Disruption".
|
// match "Disruption".
|
||||||
function closestDisposition(name) {
|
function matchDispositionSegment(norm) {
|
||||||
const norm = normalizeDispositionText(name);
|
|
||||||
if (!norm) return null;
|
if (!norm) return null;
|
||||||
let best = null, bestDist = Infinity;
|
let best = null, bestDist = Infinity;
|
||||||
for (const canonical of CANONICAL_DISPOSITIONS) {
|
for (const canonical of CANONICAL_DISPOSITIONS) {
|
||||||
@@ -168,6 +167,21 @@ function closestDisposition(name) {
|
|||||||
return bestDist <= threshold ? best : null;
|
return bestDist <= threshold ? best : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some sources list two dispositions together, e.g. "Purge the Foe
|
||||||
|
// (Reconnaissance)" (a team template noting a secondary/alternate) or
|
||||||
|
// "Take and Hold / Priority Assets". Split on the separators that join them
|
||||||
|
// and match segment-by-segment left to right, so whichever is listed first
|
||||||
|
// wins rather than whichever happens to score closer across the whole string.
|
||||||
|
function closestDisposition(name) {
|
||||||
|
const raw = String(name || "");
|
||||||
|
const segments = raw.split(/[()\/|,;]+|\s+vs\.?\s+/i).map(s => s.trim()).filter(Boolean);
|
||||||
|
for (const seg of segments.length ? segments : [raw]) {
|
||||||
|
const match = matchDispositionSegment(normalizeDispositionText(seg));
|
||||||
|
if (match) return match;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const correctDisposition = name => closestDisposition(name) || name;
|
const correctDisposition = name => closestDisposition(name) || name;
|
||||||
|
|
||||||
// The vocabulary of disposition names is whatever this event's own roster
|
// The vocabulary of disposition names is whatever this event's own roster
|
||||||
|
|||||||
Reference in New Issue
Block a user