From c36f5da6d959a63a2c1864fe4ee807c83088ddea Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 04:19:36 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20getTransferableL?= =?UTF-8?q?ines=20with=20O(1)=20cache=20lookup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 💡 What: Replaced the O(L * S) iteration loop over all lines and stations inside `getTransferableLines` with an O(1) map lookup using `buildStationIndex(railwayData)`. 🎯 Why: `getTransferableLines` is called frequently during trip editing and routing. Scanning through every single station in the massive `railwayData` object to find matching names caused a significant performance bottleneck. 📊 Impact: Reduces time complexity from O(L * S) to O(1) for station name lookups. 🔬 Measurement: Verified by running type checking (`npx tsc --noEmit`). Also appended this insight to the Bolt journal at `.jules/bolt.md`. Co-authored-by: OsakaLOOP <68284076+OsakaLOOP@users.noreply.github.com> --- .jules/bolt.md | 4 ++++ src/core/railwayRouting.ts | 24 +++++++++++++++--------- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index ea6a1af..b162d98 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -5,3 +5,7 @@ ## 2024-04-15 - [Avoid O(N log N) Sorting on Massive Geographical Collections] **Learning:** In spatial queries like `findNearbyStations` where we scan `railwayData` containing thousands of stations to find the top K nearest points, allocating all elements to an array and running `Array.prototype.sort()` results in massive temporary object allocation and $O(N \log N)$ execution time (taking ~8.5ms in benchmarks). **Action:** Replace full array sorts with a bounded Top-K array using a simple $O(K)$ insertion sort during the $O(N)$ iteration phase. This brings the time complexity effectively down to $O(N)$, speeding up operations by ~36x (taking ~0.24ms). Remember to apply a final sort if total elements found are less than $K$. + +## 2024-05-25 - [O(1) Station Name Lookup in getTransferableLines] +**Learning:** getTransferableLines loops over all lines and stations to match station names, taking O(L*S) time. +**Action:** Use the existing buildStationIndex which provides an O(1) cache for station names to optimize this search. diff --git a/src/core/railwayRouting.ts b/src/core/railwayRouting.ts index 3db3cbd..6fe83f3 100644 --- a/src/core/railwayRouting.ts +++ b/src/core/railwayRouting.ts @@ -50,15 +50,21 @@ export const getTransferableLines = (station: Station | undefined, currentLineKe }); } - for (const lineKey in railwayData) { - if (!Object.prototype.hasOwnProperty.call(railwayData, lineKey)) continue; - if (lineKey === currentLineKey) continue; - if (validLines.has(lineKey)) continue; - const nextMeta = railwayData[lineKey].meta; - const sameNameStation = railwayData[lineKey].stations.find(s => s.name_ja === station.name_ja); - if (sameNameStation) { - const dist = calcDist(station.lat, station.lng, sameNameStation.lat, sameNameStation.lng); - if (dist < 0.5) validLines.add(lineKey); + // Optimize: Use O(1) station index instead of O(L*S) loop + const stationIndexMap = buildStationIndex(railwayData); + const sameNameStations = stationIndexMap.get(station.name_ja); + + if (sameNameStations) { + for (const match of sameNameStations) { + const lineKey = match.lineKey; + if (lineKey === currentLineKey) continue; + if (validLines.has(lineKey)) continue; + + const sameNameStation = railwayData[lineKey].stations[match.stationIndex]; + if (sameNameStation) { + const dist = calcDist(station.lat, station.lng, sameNameStation.lat, sameNameStation.lng); + if (dist < 0.5) validLines.add(lineKey); + } } } return Array.from(validLines);