[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"article-object-groupby-replace-reduce":32,"code:js:true:1764qyr":235,"code:js:true:1awz7r1":236,"code:js:true:ycia3g":237,"code:js:true:197m6p8":238,"code:js:true:qtg23m":239,"code:js:true:11rscnb":240,"code:js:true:18n40ev":241},[4,20],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Build. Learn. Ship.","Practical courses, engineering-grade articles and open-source tools for people who ship.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Structured paths, listening drills and spaced repetition that actually sticks.","amber-400","⌘",[13,15,29],"es",{"courses":17,"paths":17,"articles":18,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":17},2,{"id":33,"slug":34,"title":35,"subtitle":36,"excerpt":37,"coverUrl":38,"locale":13,"readingMinutes":39,"publishedAt":40,"viewCount":41,"likeCount":19,"commentCount":19,"author":42,"vertical":47,"topic":48,"tags":51,"_count":63,"playground":65,"body":67,"bodyMd":217,"seo":218,"translationGroupId":222,"thread":223,"assessments":225,"translations":231,"quiz":233},"019fe660-2b2e-71fb-a0d1-fd1509765d3b","object-groupby-replace-reduce","You've been writing this `reduce` a hundred times. `Object.groupBy` does it in one.",null,"Grouping an array by a key is one of the most common data transforms in frontend code. You've been wiring it by hand with `reduce` for years. `Object.groupBy` is the native version — no helper, no library, no ceremony.","\u002Fmedia\u002Fcovers\u002Fobject-groupby-replace-reduce.png",4,"2026-07-14T08:16:56.309Z",28,{"id":43,"name":44,"username":45,"avatarUrl":36,"headline":46},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":49,"name":50},"javascript","JavaScript",[52,54,57,60],{"slug":49,"name":53,"color":36},"Javascript",{"slug":55,"name":56,"color":36},"webdev","Webdev",{"slug":58,"name":59,"color":36},"frontend","Frontend",{"slug":61,"name":62,"color":36},"node","Node",{"assessments":64},1,{"slug":34,"title":66},"Object.groupBy — group an array in one line",{"blocks":68,"version":64},[69,73,76,82,85,89,92,95,100,104,107,111,114,117,120,123,126,129,133,136,140,143,146,149,153,156,159,163,166,169,172,175,178,181,184,187,190,193,196,199,202,205,208],{"id":70,"html":71,"type":72},"b1","\u003Cp>Open any frontend codebase and search for \u003Ccode>.reduce\u003C\u002Fcode>. I&#39;ll wait.\u003C\u002Fp>","paragraph",{"id":74,"html":75,"type":72},"b2","\u003Cp>A meaningful fraction of those hits look like this:\u003C\u002Fp>",{"id":77,"code":78,"type":79,"language":80,"highlight":81},"b3","const byStatus = tasks.reduce((acc, task) => {\n  const key = task.status;\n  if (!acc[key]) acc[key] = [];\n  acc[key].push(task);\n  return acc;\n}, {});","code","js",[],{"id":83,"html":84,"type":72},"b4","\u003Cp>Or, if the author has been around long enough, this tighter variant:\u003C\u002Fp>",{"id":86,"code":87,"type":79,"language":80,"highlight":88},"b5","const byStatus = tasks.reduce((acc, task) => {\n  (acc[task.status] ??= []).push(task);\n  return acc;\n}, {});",[],{"id":90,"html":91,"type":72},"b6","\u003Cp>Both do the same thing: group an array by a key. It&#39;s one of the most common data transforms in any frontend codebase — group tasks by status, events by date, errors by code, users by role. You&#39;ve written it dozens of times because JavaScript never had a built-in name for it.\u003C\u002Fp>",{"id":93,"html":94,"type":72},"b7","\u003Cp>It does now. And it has been in every major browser since early 2024.\u003C\u002Fp>",{"id":96,"html":97,"text":98,"type":99,"level":31},"b8","\u003Ccode>Object.groupBy\u003C\u002Fcode> — the one-liner","Object.groupBy — the one-liner","heading",{"id":101,"code":102,"type":79,"language":80,"highlight":103},"b9","const byStatus = Object.groupBy(tasks, task => task.status);",[],{"id":105,"html":106,"type":72},"b10","\u003Cp>Pass an iterable and a callback. The callback returns the group key for each item. The result is a plain object where each key holds an array of the items that mapped to it.\u003C\u002Fp>",{"id":108,"code":109,"type":79,"language":80,"highlight":110},"b11","const tasks = [\n  { id: 1, status: 'done',        title: 'Ship it' },\n  { id: 2, status: 'todo',        title: 'Write tests' },\n  { id: 3, status: 'done',        title: 'Fix the bug' },\n  { id: 4, status: 'in-progress', title: 'Review the PR' },\n];\n\nconst grouped = Object.groupBy(tasks, t => t.status);\n\u002F\u002F {\n\u002F\u002F   done:         [{ id: 1, … }, { id: 3, … }],\n\u002F\u002F   todo:         [{ id: 2, … }],\n\u002F\u002F   'in-progress': [{ id: 4, … }],\n\u002F\u002F }",[],{"id":112,"html":113,"type":72},"b12","\u003Cp>The callback can return any value that stringifies to a useful key — a status enum, a date string, a category slug, a computed bucket. Items that map to the same string land in the same array. Items that would map to \u003Ccode>undefined\u003C\u002Fcode> land under the key \u003Ccode>&quot;undefined&quot;\u003C\u002Fcode>, which is a sign you should add a fallback in the callback.\u003C\u002Fp>",{"id":115,"html":116,"type":72},"b13","\u003C!-- playground:start -->",{"id":118,"html":119,"text":119,"type":99,"level":31},"b14","🎮 Try it yourself",{"id":121,"html":122,"type":72},"b15","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fposts\u002F2026-07-14-object-groupby-replace-reduce\u002Fplayground\u002F\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":124,"html":125,"type":72},"b16","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":127,"html":128,"type":72},"b17","\u003C!-- playground:end -->",{"id":130,"html":131,"text":132,"type":99,"level":31},"b18","When you need non-string keys: \u003Ccode>Map.groupBy\u003C\u002Fcode>","When you need non-string keys: Map.groupBy",{"id":134,"html":135,"type":72},"b19","\u003Cp>\u003Ccode>Object.groupBy\u003C\u002Fcode> coerces every key to a string. That covers most cases. If you need to group by something that isn&#39;t meaningfully stringifiable — an object reference, a boolean you want distinct from the string \u003Ccode>&quot;true&quot;\u003C\u002Fcode>, or any value where identity matters — \u003Ccode>Map.groupBy\u003C\u002Fcode> is the variant:\u003C\u002Fp>",{"id":137,"code":138,"type":79,"language":80,"highlight":139},"b20","const byPriority = Map.groupBy(tasks, task => task.priority > 5);\n\nbyPriority.get(true);  \u002F\u002F high-priority tasks\nbyPriority.get(false); \u002F\u002F everything else",[],{"id":141,"html":142,"type":72},"b21","\u003Cp>The result is a real \u003Ccode>Map\u003C\u002Fcode>, so you access values with \u003Ccode>.get()\u003C\u002Fcode> and iterate with \u003Ccode>.entries()\u003C\u002Fcode>. Keys are the exact values your callback returned — no stringification, no coercion, no surprises.\u003C\u002Fp>",{"id":144,"html":145,"text":145,"type":99,"level":31},"b22","A real-world example: the status board",{"id":147,"html":148,"type":72},"b23","\u003Cp>Before \u003Ccode>groupBy\u003C\u002Fcode>, building a Kanban-style board from a flat API response involved one of two patterns. Either a \u003Ccode>filter\u003C\u002Fcode> per lane:\u003C\u002Fp>",{"id":150,"code":151,"type":79,"language":80,"highlight":152},"b24","const lanes = ['todo', 'in-progress', 'review', 'done'];\nconst grouped = Object.fromEntries(\n  lanes.map(status => [status, tasks.filter(t => t.status === status)])\n);",[],{"id":154,"html":155,"type":72},"b25","\u003Cp>That walks \u003Ccode>tasks\u003C\u002Fcode> once per lane — four passes for four columns. Or a \u003Ccode>reduce\u003C\u002Fcode> — one pass, but four lines of bookkeeping per developer who reads it.\u003C\u002Fp>",{"id":157,"html":158,"type":72},"b26","\u003Cp>With \u003Ccode>Object.groupBy\u003C\u002Fcode>:\u003C\u002Fp>",{"id":160,"code":161,"type":79,"language":80,"highlight":162},"b27","const grouped = Object.groupBy(tasks, t => t.status);\nconst lanes = ['todo', 'in-progress', 'review', 'done'];\n\u002F\u002F `grouped` already has everything; `lanes` just controls render order",[],{"id":164,"html":165,"type":72},"b28","\u003Cp>One pass. No ceremony. The intent is in the function name.\u003C\u002Fp>",{"id":167,"html":168,"text":168,"type":99,"level":31},"b29","One subtle thing about the returned object",{"id":170,"html":171,"type":72},"b30","\u003Cp>\u003Ccode>Object.groupBy\u003C\u002Fcode> returns an object with a \u003Cstrong>null prototype\u003C\u002Fstrong> — \u003Ccode>Object.create(null)\u003C\u002Fcode>. It has no inherited \u003Ccode>toString\u003C\u002Fcode>, \u003Ccode>hasOwnProperty\u003C\u002Fcode>, or any of the usual \u003Ccode>Object.prototype\u003C\u002Fcode> methods. It&#39;s a pure data dictionary.\u003C\u002Fp>",{"id":173,"html":174,"type":72},"b31","\u003Cp>For most real uses this is exactly right: no prototype key collisions, no need for the \u003Ccode>hasOwnProperty\u003C\u002Fcode> dance before accessing a key. If downstream code needs a normal prototype — say, you&#39;re serializing it with a library that calls \u003Ccode>toString\u003C\u002Fcode> — wrap it: \u003Ccode>Object.assign({}, grouped)\u003C\u002Fcode>. But in practice you rarely need to.\u003C\u002Fp>",{"id":176,"html":177,"text":177,"type":99,"level":31},"b32","Support",{"id":179,"html":180,"type":72},"b33","\u003Cp>\u003Ccode>Object.groupBy\u003C\u002Fcode> is \u003Cstrong>Baseline 2024\u003C\u002Fstrong>: Chrome 117, Firefox 119, Safari 17.4, Node.js 21. If your targets include browsers from the last two years you can use it today without a polyfill. For older targets a one-liner shim is trivial, but at this point you&#39;re more likely to just set a browserslist target that excludes the handful of users still on pre-2024 releases.\u003C\u002Fp>",{"id":182,"html":183,"type":72},"b34","\u003C!-- quiz:start -->",{"id":185,"html":186,"text":186,"type":99,"level":31},"b35","🧠 Test yourself",{"id":188,"html":189,"type":72},"b36","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fquiz\u002Ftake.html?post=2026-07-14-object-groupby-replace-reduce\">Take the 6-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":191,"html":192,"type":72},"b37","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":194,"html":195,"type":72},"b38","\u003C!-- quiz:end -->",{"id":197,"html":198,"text":198,"type":99,"level":31},"b39","The takeaway",{"id":200,"html":201,"type":72},"b40","\u003Cp>\u003Ccode>Object.groupBy\u003C\u002Fcode> isn&#39;t a convenience — it&#39;s the word for what you&#39;ve been spelling out in four lines of \u003Ccode>reduce\u003C\u002Fcode>. Find the grouping reduces in your codebase, replace them, and delete whatever \u003Ccode>groupBy\u003C\u002Fcode> helper you wrote to avoid writing them inline. The operation has a name and a native implementation now; there&#39;s nothing left to carry.\u003C\u002Fp>",{"id":203,"type":204},"b41","divider",{"id":206,"html":207,"type":72},"b42","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":209,"type":210,"items":211,"ordered":18},"b43","list",[212,213,214,215,216],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","💼 \u003Cstrong>LinkedIn\u003C\u002Fstrong> — \u003Ca href=\"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fparsa-jiravand\u002F\">linkedin.com\u002Fin\u002Fparsa-jiravand\u003C\u002Fa>","✉️ \u003Cstrong>Email\u003C\u002Fstrong> (work &amp; contract inquiries): \u003Ca href=\"mailto:bestpractice2026@gmail.com\">bestpractice2026@gmail.com\u003C\u002Fa>","Open any frontend codebase and search for `.reduce`. I'll wait.\n\nA meaningful fraction of those hits look like this:\n\n```js\nconst byStatus = tasks.reduce((acc, task) => {\n  const key = task.status;\n  if (!acc[key]) acc[key] = [];\n  acc[key].push(task);\n  return acc;\n}, {});\n```\n\nOr, if the author has been around long enough, this tighter variant:\n\n```js\nconst byStatus = tasks.reduce((acc, task) => {\n  (acc[task.status] ??= []).push(task);\n  return acc;\n}, {});\n```\n\nBoth do the same thing: group an array by a key. It's one of the most common data transforms in any frontend codebase — group tasks by status, events by date, errors by code, users by role. You've written it dozens of times because JavaScript never had a built-in name for it.\n\nIt does now. And it has been in every major browser since early 2024.\n\n## `Object.groupBy` — the one-liner\n\n```js\nconst byStatus = Object.groupBy(tasks, task => task.status);\n```\n\nPass an iterable and a callback. The callback returns the group key for each item. The result is a plain object where each key holds an array of the items that mapped to it.\n\n```js\nconst tasks = [\n  { id: 1, status: 'done',        title: 'Ship it' },\n  { id: 2, status: 'todo',        title: 'Write tests' },\n  { id: 3, status: 'done',        title: 'Fix the bug' },\n  { id: 4, status: 'in-progress', title: 'Review the PR' },\n];\n\nconst grouped = Object.groupBy(tasks, t => t.status);\n\u002F\u002F {\n\u002F\u002F   done:         [{ id: 1, … }, { id: 3, … }],\n\u002F\u002F   todo:         [{ id: 2, … }],\n\u002F\u002F   'in-progress': [{ id: 4, … }],\n\u002F\u002F }\n```\n\nThe callback can return any value that stringifies to a useful key — a status enum, a date string, a category slug, a computed bucket. Items that map to the same string land in the same array. Items that would map to `undefined` land under the key `\"undefined\"`, which is a sign you should add a fallback in the callback.\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fposts\u002F2026-07-14-object-groupby-replace-reduce\u002Fplayground\u002F)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\n## When you need non-string keys: `Map.groupBy`\n\n`Object.groupBy` coerces every key to a string. That covers most cases. If you need to group by something that isn't meaningfully stringifiable — an object reference, a boolean you want distinct from the string `\"true\"`, or any value where identity matters — `Map.groupBy` is the variant:\n\n```js\nconst byPriority = Map.groupBy(tasks, task => task.priority > 5);\n\nbyPriority.get(true);  \u002F\u002F high-priority tasks\nbyPriority.get(false); \u002F\u002F everything else\n```\n\nThe result is a real `Map`, so you access values with `.get()` and iterate with `.entries()`. Keys are the exact values your callback returned — no stringification, no coercion, no surprises.\n\n## A real-world example: the status board\n\nBefore `groupBy`, building a Kanban-style board from a flat API response involved one of two patterns. Either a `filter` per lane:\n\n```js\nconst lanes = ['todo', 'in-progress', 'review', 'done'];\nconst grouped = Object.fromEntries(\n  lanes.map(status => [status, tasks.filter(t => t.status === status)])\n);\n```\n\nThat walks `tasks` once per lane — four passes for four columns. Or a `reduce` — one pass, but four lines of bookkeeping per developer who reads it.\n\nWith `Object.groupBy`:\n\n```js\nconst grouped = Object.groupBy(tasks, t => t.status);\nconst lanes = ['todo', 'in-progress', 'review', 'done'];\n\u002F\u002F `grouped` already has everything; `lanes` just controls render order\n```\n\nOne pass. No ceremony. The intent is in the function name.\n\n## One subtle thing about the returned object\n\n`Object.groupBy` returns an object with a **null prototype** — `Object.create(null)`. It has no inherited `toString`, `hasOwnProperty`, or any of the usual `Object.prototype` methods. It's a pure data dictionary.\n\nFor most real uses this is exactly right: no prototype key collisions, no need for the `hasOwnProperty` dance before accessing a key. If downstream code needs a normal prototype — say, you're serializing it with a library that calls `toString` — wrap it: `Object.assign({}, grouped)`. But in practice you rarely need to.\n\n## Support\n\n`Object.groupBy` is **Baseline 2024**: Chrome 117, Firefox 119, Safari 17.4, Node.js 21. If your targets include browsers from the last two years you can use it today without a polyfill. For older targets a one-liner shim is trivial, but at this point you're more likely to just set a browserslist target that excludes the handful of users still on pre-2024 releases.\n\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 6-question quiz →](https:\u002F\u002Fdaily-post-dev.netlify.app\u002Fquiz\u002Ftake.html?post=2026-07-14-object-groupby-replace-reduce)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n## The takeaway\n\n`Object.groupBy` isn't a convenience — it's the word for what you've been spelling out in four lines of `reduce`. Find the grouping reduces in your codebase, replace them, and delete whatever `groupBy` helper you wrote to avoid writing them inline. The operation has a name and a native implementation now; there's nothing left to carry.\n\n---\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)\n- 💼 **LinkedIn** — [linkedin.com\u002Fin\u002Fparsa-jiravand](https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fparsa-jiravand\u002F)\n- ✉️ **Email** (work & contract inquiries): [bestpractice2026@gmail.com](mailto:bestpractice2026@gmail.com)",{"title":219,"canonical":220,"description":221},"You've been writing this `reduce` a hundred times. `Object.groupBy` do","https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fobject-groupby-replace-reduce","Grouping an array by a key is one of the most common data transforms in frontend code. You've been wiring it by hand with `reduce` for years. `Object.groupBy` is the native version","019fe660-2b2e-71fb-a0d2-00d6b11f0e71",{"id":224,"locked":18},"019fe660-322b-70fa-9272-70e8f963a6f1",[226],{"id":227,"slug":34,"title":228,"_count":229},"019fe776-48e4-728a-bfdb-49298f576f76","Object.groupBy and Map.groupBy",{"questions":230},6,[232],{"locale":13,"slug":34},{"id":227,"slug":34,"title":228,"_count":234,"questionCount":230},{"questions":230},"\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> byStatus \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> tasks\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">reduce\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>acc\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> task\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> key \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> task\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>status;\n  \u003Cspan class=\"shj-kwd\">if\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">!\u003C\u002Fspan>acc\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>key\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> acc\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>key\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>;\n  acc\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>key\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">push\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>task\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> acc;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> byStatus \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> tasks\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">reduce\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>acc\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> task\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>acc\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>task\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>status\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">??=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">push\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>task\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">return\u003C\u002Fspan> acc;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-oneline\" data-lang=\"js\">\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> byStatus \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Object\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">groupBy\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>tasks\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> task \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> task\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>status\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> tasks \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> id\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">1\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> status\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'done'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>        title\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'Ship it'\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> id\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">2\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> status\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'todo'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>        title\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'Write tests'\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> id\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">3\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> status\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'done'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>        title\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'Fix the bug'\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n  \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan> id\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-num\">4\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> status\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'in-progress'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> title\u003Cspan class=\"shj-oper\">:\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'Review the PR'\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>;\n\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> grouped \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Object\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">groupBy\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>tasks\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> t \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> t\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>status\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F {\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F   done:         [{ id: 1, … }, { id: 3, … }],\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F   todo:         [{ id: 2, … }],\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F   'in-progress': [{ id: 4, … }],\n\u003C\u002Fspan>\u003Cspan class=\"shj-cmnt\">\u002F\u002F }\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> byPriority \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Map\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">groupBy\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>tasks\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> task \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> task\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>priority \u003Cspan class=\"shj-oper\">&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-num\">5\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\nbyPriority\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">get\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bool\">true\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;  \u003Cspan class=\"shj-cmnt\">\u002F\u002F high-priority tasks\n\u003C\u002Fspan>byPriority\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">get\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bool\">false\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F everything else\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> lanes \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-str\">'todo'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'in-progress'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'review'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'done'\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>;\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> grouped \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Object\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">fromEntries\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n  lanes\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">map\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>status \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>status\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> tasks\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">filter\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>t \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> t\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>status \u003Cspan class=\"shj-oper\">===\u003C\u002Fspan> status\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> grouped \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Object\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">groupBy\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>tasks\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> t \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> t\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>status\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> lanes \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-str\">'todo'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'in-progress'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'review'\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-str\">'done'\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>;\n\u003Cspan class=\"shj-cmnt\">\u002F\u002F `grouped` already has everything; `lanes` just controls render order\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>"]