From 90fc354877df80e9a158fe2f378da5cd6b4c17b8 Mon Sep 17 00:00:00 2001 From: anyulled <100741+anyulled@users.noreply.github.com> Date: Wed, 27 May 2026 08:27:47 +0000 Subject: [PATCH 1/2] feat: [performance improvement] optimize tag page filtering - Hoist `decodedTag.toLowerCase()` out of the filter block to avoid repeated allocations. - Replace duplicate `.flatMap().find(...)` over the entire dataset with extracting the display tag from the already `filteredTalks` in the page component. - Use `.find` instead of `.filter` in `generateMetadata` to short-circuit upon finding the first match. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- .jules/bolt.md | 5 +++++ app/2026/tags/[tag]/page.tsx | 19 ++++++++++++++----- app/[year]/tags/[tag]/page.tsx | 19 ++++++++++++++----- 3 files changed, 33 insertions(+), 10 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index 9345ee7a..95df2653 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -2,3 +2,8 @@ **Learning:** In Next.js/React applications, when grouping items (like schedules or talks) into a `Map` where the values are arrays, using the array spread operator `[...existing, item]` inside a loop (like `forEach` or `map`) causes amortized O(N^2) memory allocations and unnecessary Garbage Collection overhead. **Action:** Always use `.push()` on the existing array reference if the data structure permits local mutation. For strict ESLint configurations enforcing `no-restricted-syntax`, extract the existing array, push to it, and handle the fallback elegantly (`if (!existing) { map.set(key, [item]); } else { existing.push(item); }`). + +## 2025-02-23 - Optimize multiple traversals on same datasets + +**Learning:** In Next.js SSG metadata and page generation (like `app/[year]/tags/[tag]/page.tsx`), when doing filtering operations on an array of objects to get multiple subsets of data or metadata, finding a match through multiple `flatMap` and `.find` and `filter` array iterations introduces unneeded O(N) traversal overhead. +**Action:** Instead of running the same string manipulations and `.some` callback validations, combine the logic to reuse variables. E.g. hoisting `decodedTag.toLowerCase()` out of the filter block, and for finding a title tag for metadata display, extract it from `filteredTalks[0]` instead of traversing the whole original list again. Ensure `.find` is used instead of `.filter` when expecting one item or when only caring about the first match to allow short-circuiting. diff --git a/app/2026/tags/[tag]/page.tsx b/app/2026/tags/[tag]/page.tsx index 5c9a6ebc..e0f5a97c 100644 --- a/app/2026/tags/[tag]/page.tsx +++ b/app/2026/tags/[tag]/page.tsx @@ -40,8 +40,16 @@ export async function generateMetadata({ params }: { params: Promise<{ tag: stri const sessionGroups = await getTalks(year); const allTalks = sessionGroups.flatMap((group) => group.sessions); - const displayTag = - allTalks.flatMap(getTagsFromTalk).find((t) => t.replaceAll(" ", "-").toLowerCase() === decodedTag.toLowerCase()) ?? decodedTag.replaceAll("-", " "); + const targetTagNormalized = decodedTag.toLowerCase(); + + const matchedTalk = allTalks.find((talk) => { + const talkTags = getTagsFromTalk(talk); + return talkTags.some((t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized); + }); + + const displayTag = matchedTalk + ? getTagsFromTalk(matchedTalk).find((t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized) ?? decodedTag.replaceAll("-", " ") + : decodedTag.replaceAll("-", " "); return { title: `Talks tagged "${displayTag}" - DevBcn ${year}`, @@ -58,19 +66,20 @@ export default async function Page({ params }: { params: Promise<{ tag: string } const sessionGroups = await getTalks(year); const allTalks = sessionGroups.flatMap((group) => group.sessions); - const displayTag = - allTalks.flatMap(getTagsFromTalk).find((t) => t.replaceAll(" ", "-").toLowerCase() === decodedTag.toLowerCase()) ?? decodedTag.replaceAll("-", " "); + const targetTagNormalized = decodedTag.toLowerCase(); const filteredTalks = allTalks.filter((talk) => { const talkTags = getTagsFromTalk(talk); - return talkTags.some((t) => t.replaceAll(" ", "-").toLowerCase() === decodedTag.toLowerCase()); + return talkTags.some((t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized); }); if (filteredTalks.length === 0) { notFound(); } + const displayTag = getTagsFromTalk(filteredTalks[0]).find((t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized) ?? decodedTag.replaceAll("-", " "); + return (
{/* Header */} diff --git a/app/[year]/tags/[tag]/page.tsx b/app/[year]/tags/[tag]/page.tsx index 35c74f61..82674753 100644 --- a/app/[year]/tags/[tag]/page.tsx +++ b/app/[year]/tags/[tag]/page.tsx @@ -47,8 +47,16 @@ export async function generateMetadata({ params }: Readonly): Prom const sessionGroups = await getTalks(year); const allTalks = sessionGroups.flatMap((group) => group.sessions); - const displayTag = - allTalks.flatMap(getTagsFromTalk).find((t) => t.replaceAll(" ", "-").toLowerCase() === decodedTag.toLowerCase()) ?? decodedTag.replaceAll("-", " "); + const targetTagNormalized = decodedTag.toLowerCase(); + + const matchedTalk = allTalks.find((talk) => { + const talkTags = getTagsFromTalk(talk); + return talkTags.some((t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized); + }); + + const displayTag = matchedTalk + ? getTagsFromTalk(matchedTalk).find((t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized) ?? decodedTag.replaceAll("-", " ") + : decodedTag.replaceAll("-", " "); return { title: `Talks tagged "${displayTag}" - DevBcn ${year}`, @@ -64,19 +72,20 @@ export default async function TagPage({ params }: Readonly) { const sessionGroups = await getTalks(year); const allTalks = sessionGroups.flatMap((group) => group.sessions); - const displayTag = - allTalks.flatMap(getTagsFromTalk).find((t) => t.replaceAll(" ", "-").toLowerCase() === decodedTag.toLowerCase()) ?? decodedTag.replaceAll("-", " "); + const targetTagNormalized = decodedTag.toLowerCase(); const filteredTalks = allTalks.filter((talk) => { const talkTags = getTagsFromTalk(talk); - return talkTags.some((t) => t.replaceAll(" ", "-").toLowerCase() === decodedTag.toLowerCase()); + return talkTags.some((t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized); }); if (filteredTalks.length === 0) { notFound(); } + const displayTag = getTagsFromTalk(filteredTalks[0]).find((t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized) ?? decodedTag.replaceAll("-", " "); + return (
{/* Header */} From 100c7173822f9c72c463d160762e23de5aa546e1 Mon Sep 17 00:00:00 2001 From: anyulled <100741+anyulled@users.noreply.github.com> Date: Wed, 27 May 2026 08:34:44 +0000 Subject: [PATCH 2/2] feat: [performance improvement] optimize tag page filtering - Hoist `decodedTag.toLowerCase()` out of the filter block to avoid repeated allocations. - Replace duplicate `.flatMap().find(...)` over the entire dataset with extracting the display tag from the already `filteredTalks` in the page component. - Use `.find` instead of `.filter` in `generateMetadata` to short-circuit upon finding the first match. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- app/2026/tags/[tag]/page.tsx | 5 +++-- app/[year]/tags/[tag]/page.tsx | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/2026/tags/[tag]/page.tsx b/app/2026/tags/[tag]/page.tsx index e0f5a97c..65d624e8 100644 --- a/app/2026/tags/[tag]/page.tsx +++ b/app/2026/tags/[tag]/page.tsx @@ -48,7 +48,7 @@ export async function generateMetadata({ params }: { params: Promise<{ tag: stri }); const displayTag = matchedTalk - ? getTagsFromTalk(matchedTalk).find((t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized) ?? decodedTag.replaceAll("-", " ") + ? (getTagsFromTalk(matchedTalk).find((t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized) ?? decodedTag.replaceAll("-", " ")) : decodedTag.replaceAll("-", " "); return { @@ -78,7 +78,8 @@ export default async function Page({ params }: { params: Promise<{ tag: string } notFound(); } - const displayTag = getTagsFromTalk(filteredTalks[0]).find((t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized) ?? decodedTag.replaceAll("-", " "); + const displayTag = + getTagsFromTalk(filteredTalks[0]).find((t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized) ?? decodedTag.replaceAll("-", " "); return (
diff --git a/app/[year]/tags/[tag]/page.tsx b/app/[year]/tags/[tag]/page.tsx index 82674753..a73632a1 100644 --- a/app/[year]/tags/[tag]/page.tsx +++ b/app/[year]/tags/[tag]/page.tsx @@ -55,7 +55,7 @@ export async function generateMetadata({ params }: Readonly): Prom }); const displayTag = matchedTalk - ? getTagsFromTalk(matchedTalk).find((t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized) ?? decodedTag.replaceAll("-", " ") + ? (getTagsFromTalk(matchedTalk).find((t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized) ?? decodedTag.replaceAll("-", " ")) : decodedTag.replaceAll("-", " "); return { @@ -84,7 +84,8 @@ export default async function TagPage({ params }: Readonly) { notFound(); } - const displayTag = getTagsFromTalk(filteredTalks[0]).find((t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized) ?? decodedTag.replaceAll("-", " "); + const displayTag = + getTagsFromTalk(filteredTalks[0]).find((t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized) ?? decodedTag.replaceAll("-", " "); return (