From 6678384895510033818218e56ece45afdf97c09c Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 12 Apr 2026 03:21:56 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Avoid=20Object.values=20arr?= =?UTF-8?q?ay=20allocations=20using=20for-in=20loops?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaced Object.values.some() and Object.values.forEach() with early breaking for...in loops. This avoids array allocation on every call and allows us to exit early without full O(N) array traversals, lowering memory overhead and improving UI performance when iterating over large `railwayData` datasets. Co-authored-by: OsakaLOOP <68284076+OsakaLOOP@users.noreply.github.com> --- src/AppLayout.tsx | 14 ++++++-- src/RailRound.jsx | 35 +++++++++++++++----- src/components/modals/WalkTripEditor.tsx | 42 +++++++++++++++++------- src/pages/TripsPage.tsx | 22 +++++++++---- 4 files changed, 83 insertions(+), 30 deletions(-) diff --git a/src/AppLayout.tsx b/src/AppLayout.tsx index bca5cbd..363b084 100644 --- a/src/AppLayout.tsx +++ b/src/AppLayout.tsx @@ -607,9 +607,17 @@ export const AppLayout: React.FC = () => { const currentRailwayData = useStore.getState().railwayData; // Only trigger if we have data and missing distances - const needsCalc = Object.values(currentRailwayData).some(line => - line.stations.length > 1 && line.stations[0].distToNext === undefined - ); + // ⚡ Bolt: Replaced Object.values.some() with early-breaking for-in loop to prevent large array allocation. + let needsCalc = false; + for (const lineKey in currentRailwayData) { + if (Object.prototype.hasOwnProperty.call(currentRailwayData, lineKey)) { + const line = currentRailwayData[lineKey]; + if (line.stations && line.stations.length > 1 && line.stations[0].distToNext === undefined) { + needsCalc = true; + break; + } + } + } if (needsCalc) { const showFakeProgress = useStore.getState().showFakeProgress; diff --git a/src/RailRound.jsx b/src/RailRound.jsx index 09b7571..54fd600 100644 --- a/src/RailRound.jsx +++ b/src/RailRound.jsx @@ -1265,12 +1265,22 @@ const RecordsView = ({ trips, railwayData, setTrips, onEdit, onDelete, onAdd, se if (isWalk) { let startName = t.fromId || ''; let endName = t.toId || ''; - Object.values(railwayData).forEach(line => { - const s = line.stations.find(st => st.id === t.fromId); - if (s) startName = s.name_ja; - const e = line.stations.find(st => st.id === t.toId); - if (e) endName = e.name_ja; - }); + let foundStart = false; + let foundEnd = false; + // ⚡ Bolt: Replaced Object.values(railwayData).forEach with early-breaking for-in loop to prevent large array allocation and unnecessary traversal. + for (const lineKey in railwayData) { + if (!Object.prototype.hasOwnProperty.call(railwayData, lineKey)) continue; + const line = railwayData[lineKey]; + if (!foundStart) { + const s = line.stations.find(st => st.id === t.fromId); + if (s) { startName = s.name_ja; foundStart = true; } + } + if (!foundEnd) { + const e = line.stations.find(st => st.id === t.toId); + if (e) { endName = e.name_ja; foundEnd = true; } + } + if (foundStart && foundEnd) break; + } const isTree = t.walkType === 'tree'; const cls = { @@ -1460,9 +1470,16 @@ const StatsView = ({ trips, railwayData, geoData, user, userProfile, segmentGeom let count = 0; if (railwayData) { const uniqueStations = new Set(); - Object.values(railwayData).forEach(line => { - if (line.stations) line.stations.forEach(s => uniqueStations.add(s.id)); - }); + // ⚡ Bolt: Replaced Object.values.forEach and inner map iteration with faster for-in and classic for loop to prevent O(N) array allocation overhead. + for (const lineKey in railwayData) { + if (!Object.prototype.hasOwnProperty.call(railwayData, lineKey)) continue; + const line = railwayData[lineKey]; + if (line.stations) { + for (let i = 0; i < line.stations.length; i++) { + uniqueStations.add(line.stations[i].id); + } + } + } count = uniqueStations.size; } return count; diff --git a/src/components/modals/WalkTripEditor.tsx b/src/components/modals/WalkTripEditor.tsx index d2a25eb..4ac6260 100644 --- a/src/components/modals/WalkTripEditor.tsx +++ b/src/components/modals/WalkTripEditor.tsx @@ -79,12 +79,20 @@ 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: Replaced Object.values.forEach with early-breaking for-in loop. + for (const lineKey in railwayData) { + if (!Object.prototype.hasOwnProperty.call(railwayData, lineKey)) continue; + const line = railwayData[lineKey]; + if (!startCoords) { + const s = line.stations.find(st => st.id === form.fromId); + if (s) startCoords = [s.lng, s.lat]; + } + if (!endCoords) { + const e = line.stations.find(st => st.id === form.toId); + if (e) endCoords = [e.lng, e.lat]; + } + if (startCoords && endCoords) break; + } if (startCoords && endCoords) { walkPath = generateBezierPath(startCoords as [number, number], endCoords as [number, number]); @@ -127,12 +135,22 @@ 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; - }); + let foundStart = false; + let foundEnd = false; + // ⚡ Bolt: Replaced Object.values.forEach with early-breaking for-in loop to prevent large array allocation. + for (const lineKey in railwayData) { + if (!Object.prototype.hasOwnProperty.call(railwayData, lineKey)) continue; + const line = railwayData[lineKey]; + if (!foundStart) { + const s = line.stations.find(st => st.id === form.fromId); + if (s) { startName = s.name_ja; foundStart = true; } + } + if (!foundEnd) { + const e = line.stations.find(st => st.id === form.toId); + if (e) { endName = e.name_ja; foundEnd = true; } + } + if (foundStart && foundEnd) break; + } const isTree = form.walkType === 'tree'; diff --git a/src/pages/TripsPage.tsx b/src/pages/TripsPage.tsx index a033db8..7a605a5 100644 --- a/src/pages/TripsPage.tsx +++ b/src/pages/TripsPage.tsx @@ -166,12 +166,22 @@ 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; - }); + let foundStart = false; + let foundEnd = false; + // ⚡ Bolt: Replaced Object.values.forEach with early-breaking for-in loop to avoid allocating large arrays. + for (const lineKey in railwayData) { + if (!Object.prototype.hasOwnProperty.call(railwayData, lineKey)) continue; + const line = railwayData[lineKey]; + if (!foundStart) { + const s = line.stations.find(st => st.id === trip.fromId); + if (s) { startName = s.name_ja; foundStart = true; } + } + if (!foundEnd) { + const e = line.stations.find(st => st.id === trip.toId); + if (e) { endName = e.name_ja; foundEnd = true; } + } + if (foundStart && foundEnd) break; + } const isTree = trip.walkType === 'tree'; const cls = {