Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 2026-03-31 - [Optimized Iteration over large collections]
**Learning:** Found an opportunity to replace chained array methods like flatMap, map, reduce, and Object.values/keys with single-pass manual 'for' and 'for...in' loops when processing arrays and objects to avoid allocating large temporary data structures. Used ES6 Maps/Sets where efficient counting/deduplication was needed.
**Action:** Apply this pattern to other performance sensitive areas where objects and arrays map over large datasets, and ensure that iteration checks 'Object.prototype.hasOwnProperty.call()' when utilizing 'for...in'. Note: This project lacks a package.json at the root so standard npm/pnpm lint tools might not be readily available.
## 2026-04-10 - [O(1) Early Exiting for React Loop Renders]
**Learning:** Found multiple instances where large constant data structures (like `railwayData`) were mapped using full `Object.values().forEach()` combined with `array.find()`, without any capability to early break, directly inside render cycles or callbacks (`TripsPage.tsx`, `WalkTripEditor.tsx`, `StationMenu.jsx`). This unnecessarily iterated over every line on every render even after the station was found.
**Action:** Replaced these full-sweep iterator methods with standard unrolled `for...in` and `for` loops, adding explicit `break` logic once target items are found. This acts as an immediate O(1) circuit breaker in the best cases, dramatically lowering unneeded execution cycles.
17 changes: 14 additions & 3 deletions src/components/StationMenu.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,20 @@ const StationMenu = ({ position, stationData, railwayData, onClose }) => {
// 1. Identify all lines for this station
const lines = [];
if (railwayData) {
Object.keys(railwayData).forEach(key => {
// ⚡ Bolt: Fast iterative search replacing Object.keys + array.find
for (const key in railwayData) {
if (!Object.prototype.hasOwnProperty.call(railwayData, key)) continue;
const line = railwayData[key];
const match = line.stations.find(s => s.name_ja === stationData.name_ja);
if (!line.stations) continue;

let match = null;
for (let i = 0; i < line.stations.length; i++) {
if (line.stations[i].name_ja === stationData.name_ja) {
match = line.stations[i];
break;
}
}

if (match) {
lines.push({
lineKey: key,
Expand All @@ -69,7 +80,7 @@ const StationMenu = ({ position, stationData, railwayData, onClose }) => {
company: line.meta.company
});
}
});
}
}

if (lines.length === 0) return null;
Expand Down
54 changes: 42 additions & 12 deletions src/components/modals/WalkTripEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,25 @@ export const WalkTripEditor: React.FC = () => {
// Find coordinates for the Bezier curve
let startCoords = null;
let endCoords = null;
Object.values(railwayData).forEach(line => {
const s = line.stations.find(st => st.id === form.fromId);
if (s) startCoords = [s.lng, s.lat];
const e = line.stations.find(st => st.id === form.toId);
if (e) endCoords = [e.lng, e.lat];
});

// ⚡ Bolt: Fast iterative search replacing Object.values + array.find
for (const lineKey in railwayData) {
if (!Object.prototype.hasOwnProperty.call(railwayData, lineKey)) continue;
const stations = railwayData[lineKey].stations;
if (!stations) continue;

for (let i = 0; i < stations.length; i++) {
const st = stations[i];
if (!startCoords && st.id === form.fromId) {
startCoords = [st.lng, st.lat];
}
if (!endCoords && st.id === form.toId) {
endCoords = [st.lng, st.lat];
}
if (startCoords && endCoords) break;
}
if (startCoords && endCoords) break;
}

if (startCoords && endCoords) {
walkPath = generateBezierPath(startCoords as [number, number], endCoords as [number, number]);
Expand Down Expand Up @@ -127,12 +140,29 @@ export const WalkTripEditor: React.FC = () => {
// Resolving station names for read-only display
let startName = t('walk.unknownStart', "未知起点");
let endName = t('walk.unknownEnd', "未知终点");
Object.values(railwayData).forEach(line => {
const s = line.stations.find(st => st.id === form.fromId);
if (s) startName = s.name_ja;
const e = line.stations.find(st => st.id === form.toId);
if (e) endName = e.name_ja;
});

// ⚡ Bolt: Fast iterative search replacing Object.values + array.find
let foundStartName = false;
let foundEndName = false;
for (const lineKey in railwayData) {
if (!Object.prototype.hasOwnProperty.call(railwayData, lineKey)) continue;
const stations = railwayData[lineKey].stations;
if (!stations) continue;

for (let i = 0; i < stations.length; i++) {
const st = stations[i];
if (!foundStartName && st.id === form.fromId) {
startName = st.name_ja;
foundStartName = true;
}
if (!foundEndName && st.id === form.toId) {
endName = st.name_ja;
foundEndName = true;
}
if (foundStartName && foundEndName) break;
}
if (foundStartName && foundEndName) break;
}

const isTree = form.walkType === 'tree';

Expand Down
29 changes: 23 additions & 6 deletions src/pages/TripsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,29 @@ export const TripsPage: React.FC = () => {
if (isWalk) {
let startName = trip.fromId || '';
let endName = trip.toId || '';
Object.values(railwayData).forEach(line => {
const s = line.stations.find(st => st.id === trip.fromId);
if (s) startName = s.name_ja;
const e = line.stations.find(st => st.id === trip.toId);
if (e) endName = e.name_ja;
});

// ⚡ Bolt: Fast iterative search replacing Object.values + array.find
let foundStart = false;
let foundEnd = false;
for (const lineKey in railwayData) {
if (!Object.prototype.hasOwnProperty.call(railwayData, lineKey)) continue;
const stations = railwayData[lineKey].stations;
if (!stations) continue;

for (let i = 0; i < stations.length; i++) {
const st = stations[i];
if (!foundStart && st.id === trip.fromId) {
startName = st.name_ja;
foundStart = true;
}
if (!foundEnd && st.id === trip.toId) {
endName = st.name_ja;
foundEnd = true;
}
if (foundStart && foundEnd) break;
}
if (foundStart && foundEnd) break;
}

const isTree = trip.walkType === 'tree';
const cls = {
Expand Down