Skip to content

Commit 3dc9b68

Browse files
committed
Refactor findStepColumns
- Map better represents key optionality - Set lets us not use lodash without - chooseStep moves that BL to a pure function
1 parent 2093388 commit 3dc9b68

File tree

3 files changed

+35
-27
lines changed

3 files changed

+35
-27
lines changed

src/components/pnp/findStepColumns.ts

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { sortBy } from '@seedcompany/common';
22
import levenshtein from 'fastest-levenshtein';
3-
import { startCase, without } from 'lodash';
3+
import { startCase } from 'lodash';
44
import { type Column } from '~/common/xlsx.util';
55
import { ProductStep as Step } from '../product/dto';
66
import { type PnpExtractionResult, PnpProblemType } from './extraction-result';
@@ -15,8 +15,8 @@ export function findStepColumns(
1515
result?: PnpExtractionResult,
1616
availableSteps: readonly Step[] = [...Step],
1717
) {
18-
const matchedColumns: Partial<Record<Step, Column>> = {};
19-
let remainingSteps = availableSteps;
18+
const matchedColumns = new Map<Step, Column>();
19+
const remainingSteps = new Set(availableSteps);
2020
const possibleSteps = sheet.stepLabels
2121
.walkRight()
2222
.filter((cell) => !!cell.asString)
@@ -25,33 +25,41 @@ export function findStepColumns(
2525
possibleSteps.forEach(({ label, column, cell }, index) => {
2626
if (index === possibleSteps.length - 1) {
2727
// The last step should always be called Completed in CORD per Seth.
28-
// Written PnP already match, but OBS calls it Record. This is mislabeled
29-
// depending on the methodology.
30-
matchedColumns[Step.Completed] = column;
28+
// Written PnP already matches, but OBS calls it Record.
29+
// This is mislabeled depending on the methodology.
30+
matchedColumns.set(Step.Completed, column);
3131
return;
3232
}
33-
const distances = remainingSteps.map((step) => {
34-
const humanLabel = startCase(step).replace(' And ', ' & ');
35-
const distance = levenshtein.distance(label, humanLabel);
36-
return [step, distance] as const;
37-
});
38-
// Pick the step that is the closest fuzzy match
39-
const chosen = sortBy(
40-
// 5 is too far ignore those
41-
distances.filter(([_, distance]) => distance < 5),
42-
([_, distance]) => distance,
43-
)[0]?.[0];
33+
34+
const chosen = chooseStep(label, remainingSteps);
4435
if (!chosen) {
4536
result?.addProblem(NonStandardStep, cell, { label });
4637
return;
4738
}
48-
matchedColumns[chosen] = column;
49-
50-
remainingSteps = without(remainingSteps, chosen);
39+
matchedColumns.set(chosen, column);
40+
remainingSteps.delete(chosen);
5141
});
52-
return matchedColumns as Record<Step, Column>;
42+
return matchedColumns as ReadonlyMap<Step, Column>;
5343
}
5444

45+
const chooseStep = (
46+
label: string,
47+
available: ReadonlySet<Step>,
48+
): Step | undefined => {
49+
const distances = available.values().map((step) => {
50+
const humanLabel = startCase(step).replace(' And ', ' & ');
51+
const distance = levenshtein.distance(label, humanLabel);
52+
return { step, distance };
53+
});
54+
// Pick the step that is the closest fuzzy match
55+
const chosen = sortBy(
56+
// 5 is too far ignoring those
57+
distances.filter(({ distance }) => distance < 5),
58+
({ distance }) => distance,
59+
).at(0);
60+
return chosen?.step;
61+
};
62+
5563
const NonStandardStep = PnpProblemType.register({
5664
name: 'NonStandardStep',
5765
severity: 'Error',

src/components/product-progress/step-progress-extractor.service.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ export class StepProgressExtractor {
6666
const parseProgressRow =
6767
(
6868
pnp: Pnp,
69-
stepColumns: Record<Step, Column>,
70-
planningStepColumns: Record<Step, Column>,
69+
stepColumns: ReadonlyMap<Step, Column>,
70+
planningStepColumns: ReadonlyMap<Step, Column>,
7171
result: PnpProgressExtractionResult,
7272
) =>
7373
(cell: Cell<ProgressSheet>, index: number): ExtractedRow => {
@@ -81,7 +81,7 @@ const parseProgressRow =
8181
const steps = entries(stepColumns).flatMap<StepProgressInput>(
8282
([step, column]) => {
8383
const fiscalYear = pnp.planning.cell(
84-
planningStepColumns[step],
84+
planningStepColumns.get(step)!,
8585
planningRow,
8686
);
8787

src/components/product/product.extractor.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ export class ProductExtractor {
8181
const parseProductRow =
8282
(
8383
pnp: Pnp,
84-
stepColumns: Record<Step, Column>,
85-
progressStepColumns: Record<Step, Column>,
84+
stepColumns: ReadonlyMap<Step, Column>,
85+
progressStepColumns: ReadonlyMap<Step, Column>,
8686
result: PnpPlanningExtractionResult,
8787
) =>
8888
(cell: Cell<PlanningSheet>, index: number): ExtractedRow => {
@@ -94,7 +94,7 @@ const parseProductRow =
9494
const steps = entries(stepColumns).flatMap(([step, column]) => {
9595
const plannedCell = sheet.cell(column, row);
9696
const progressCell = pnp.progress.cell(
97-
progressStepColumns[step],
97+
progressStepColumns.get(step)!,
9898
progressRow,
9999
);
100100

0 commit comments

Comments
 (0)