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
5 changes: 5 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
20 changes: 15 additions & 5 deletions app/2026/tags/[tag]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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("-", " ");
Comment on lines +43 to +52
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation performs redundant operations: it first searches for a matching talk using .find() and .some(), and then searches the tags of that talk again using .find(). This results in duplicate calls to getTagsFromTalk and redundant string replacements/lowercasing.

We can optimize this by using a simple for...of loop to find the matching tag directly in a single pass, which short-circuits immediately and avoids double-traversal.

  const targetTagNormalized = decodedTag.toLowerCase();
  let displayTag = decodedTag.replaceAll("-", " ");

  for (const talk of allTalks) {
    const matchingTag = getTagsFromTalk(talk).find(
      (t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized
    );
    if (matchingTag) {
      displayTag = matchingTag;
      break;
    }
  }


return {
title: `Talks tagged "${displayTag}" - DevBcn ${year}`,
Expand All @@ -58,19 +66,21 @@ 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 (
<div>
{/* Header */}
Expand Down
20 changes: 15 additions & 5 deletions app/[year]/tags/[tag]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,16 @@ export async function generateMetadata({ params }: Readonly<TagPageProps>): 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("-", " ");
Comment on lines +50 to +59
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation performs redundant operations: it first searches for a matching talk using .find() and .some(), and then searches the tags of that talk again using .find(). This results in duplicate calls to getTagsFromTalk and redundant string replacements/lowercasing.

We can optimize this by using a simple for...of loop to find the matching tag directly in a single pass, which short-circuits immediately and avoids double-traversal.

  const targetTagNormalized = decodedTag.toLowerCase();
  let displayTag = decodedTag.replaceAll("-", " ");

  for (const talk of allTalks) {
    const matchingTag = getTagsFromTalk(talk).find(
      (t) => t.replaceAll(" ", "-").toLowerCase() === targetTagNormalized
    );
    if (matchingTag) {
      displayTag = matchingTag;
      break;
    }
  }


return {
title: `Talks tagged "${displayTag}" - DevBcn ${year}`,
Expand All @@ -64,19 +72,21 @@ export default async function TagPage({ params }: Readonly<TagPageProps>) {
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 (
<div>
{/* Header */}
Expand Down
Loading