diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 0000000..470eab8 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-10-01 - O(N*M) Lookup Optimization +**Learning:** Found O(N*M) time complexity loop where `Array.map` contained an `Array.find` inside a React useMemo hook (`routeStopIds.map((id) => pins.find(...))`). When replacing array lookups with memoized Maps (`useMemo`) in React components, do not use the memoized Map within imperative external bindings or global test hooks (e.g., `window.__TEST_HOOKS`, `window.__MAP_APP_TEST`) initialized outside the React render cycle, as this can introduce stale closures. Retain the original array lookups (e.g., `array.find()`) for those specific external bindings. +**Action:** Replace `Array.map` with `Array.find` using a `Map` created via `useMemo` for O(1) lookups in the component, but retain `pins.find` in `window.__MAP_APP_TEST`. diff --git a/components/territory/territory-workspace.tsx b/components/territory/territory-workspace.tsx index 6d2ff49..259abd4 100644 --- a/components/territory/territory-workspace.tsx +++ b/components/territory/territory-workspace.tsx @@ -570,13 +570,20 @@ export function TerritoryWorkspace({ orgSlug, initialDashboard, territoryConfig const mobileViewAutoSwitchedRef = useRef(initialPreferListView); const pins = data.pins; - const selectedPin = useMemo(() => pins.find((pin) => pin.id === selectedId) ?? null, [pins, selectedId]); + const pinsById = useMemo(() => { + const map = new Map(); + for (const pin of pins) { + map.set(pin.id, pin); + } + return map; + }, [pins]); + const selectedPin = useMemo(() => (selectedId ? pinsById.get(selectedId) ?? null : null), [pinsById, selectedId]); const routeStops = useMemo( () => routePlanningEnabled - ? routeStopIds.map((id) => pins.find((pin) => pin.id === id)).filter((pin): pin is TerritoryAccountPin => Boolean(pin)) + ? routeStopIds.map((id) => pinsById.get(id)).filter((pin): pin is TerritoryAccountPin => Boolean(pin)) : [], - [pins, routePlanningEnabled, routeStopIds], + [pinsById, routePlanningEnabled, routeStopIds], ); const mappablePinCount = useMemo(() => pins.filter(hasUsableCoordinates).length, [pins]); const activeFilterCount = Object.values(filters).filter(Boolean).length;