{"id":"l-js-3-3","courseId":"javascript-fundamentals","lang":"en","kind":"lesson","summary":"Summarise a list into one value with reduce, build per-category totals, and sort numbers, text and objects correctly with a comparator, without the default-sort and mutation traps.","html":"<p>Two questions every budgeting app has to answer: “how much did I spend on food?” and “what were my biggest expenses?”. The first turns a list into one number per category. The second puts the list in a useful order. JavaScript has a method for each, and each has one famous trap.</p>\n<h2 id=\"sec-1\" tabindex=\"-1\">reduce: many values into one</h2>\n<p>You already know the accumulator pattern: start with a value, update it for each item, use it at the end. <code dir=\"ltr\">reduce</code> is that pattern as a method:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"0\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"0\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> expenses</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  { label: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Coffee\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, amount: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">450</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, category: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"food\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  { label: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Train\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, amount: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1220</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, category: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"travel\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  { label: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Lunch\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, amount: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1350</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, category: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"food\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">];</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> total</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> expenses.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">reduce</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">sum</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">e</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> sum </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> e.amount, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(total);</span></span></code></pre><div class=\"run-output\" data-run-output=\"0\" hidden></div></div>\n<p><code dir=\"ltr\">reduce</code> takes two arguments: a callback and an <strong>initial value</strong> (here <code dir=\"ltr\">0</code>). It calls the callback once per item with the current <strong>accumulator</strong> (<code dir=\"ltr\">sum</code>) and the item (<code dir=\"ltr\">e</code>). Whatever the callback returns becomes the accumulator for the next call. After the last item, that final accumulator is the result.</p>\n<figure class=\"diagram\"><svg class=\"diagram-svg\" focusable=\"false\" viewBox=\"0 0 680 190\" role=\"img\" aria-labelledby=\"l-js-3-3-t1\"><defs><marker id=\"arrow-l-js-3-3-1\" viewBox=\"0 0 10 10\" refX=\"9\" refY=\"5\" markerWidth=\"7\" markerHeight=\"7\" orient=\"auto-start-reverse\"><path d=\"M0 0 L10 5 L0 10 z\" class=\"d-arrowhead\"/></marker></defs>\n  <title id=\"l-js-3-3-t1\">reduce starts with the initial value 0. The first call returns 0 plus 450, which is 450; the second returns 450 plus 1220, which is 1670; the third returns 1670 plus 1350, which is 3020, the final result.</title>\n  <rect class=\"d-box-accent\" x=\"10\" y=\"70\" width=\"80\" height=\"50\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"50\" y=\"100\" text-anchor=\"middle\">0</text>\n  <text class=\"d-label-muted\" x=\"50\" y=\"146\" text-anchor=\"middle\">initial</text>\n  <rect class=\"d-box\" x=\"130\" y=\"60\" width=\"140\" height=\"70\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"200\" y=\"90\" text-anchor=\"middle\">0 + 450</text>\n  <text class=\"d-label-muted\" x=\"200\" y=\"116\" text-anchor=\"middle\">Coffee</text>\n  <rect class=\"d-box\" x=\"310\" y=\"60\" width=\"150\" height=\"70\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"385\" y=\"90\" text-anchor=\"middle\">450 + 1220</text>\n  <text class=\"d-label-muted\" x=\"385\" y=\"116\" text-anchor=\"middle\">Train</text>\n  <rect class=\"d-box\" x=\"500\" y=\"60\" width=\"150\" height=\"70\" rx=\"10\"/>\n  <text class=\"d-code\" x=\"575\" y=\"90\" text-anchor=\"middle\">1670 + 1350</text>\n  <text class=\"d-label-muted\" x=\"575\" y=\"116\" text-anchor=\"middle\">Lunch</text>\n  <path class=\"d-arrow\" d=\"M90 95 L126 95\" marker-end=\"url(#arrow-l-js-3-3-1)\"/>\n  <path class=\"d-arrow\" d=\"M270 95 L306 95\" marker-end=\"url(#arrow-l-js-3-3-1)\"/>\n  <path class=\"d-arrow\" d=\"M460 95 L496 95\" marker-end=\"url(#arrow-l-js-3-3-1)\"/>\n  <rect class=\"d-box-success\" x=\"530\" y=\"150\" width=\"90\" height=\"34\" rx=\"8\"/>\n  <text class=\"d-code\" x=\"575\" y=\"172\" text-anchor=\"middle\">3020</text>\n  <path class=\"d-arrow\" d=\"M575 130 L575 146\" marker-end=\"url(#arrow-l-js-3-3-1)\"/>\n  <text class=\"d-label-muted\" x=\"200\" y=\"40\" text-anchor=\"middle\">returns 450</text>\n  <text class=\"d-label-muted\" x=\"385\" y=\"40\" text-anchor=\"middle\">returns 1670</text>\n</svg><figcaption>reduce passes the accumulator from one call to the next</figcaption></figure>\n<p>Always pass the initial value. Without it, <code dir=\"ltr\">reduce</code> uses the first item as the starting accumulator, which here would be a whole expense object rather than a number, and on an empty list it throws <code dir=\"ltr\">TypeError: Reduce of empty array with no initial value</code>.</p>\n<h3 id=\"sec-2\" tabindex=\"-1\">Grouping: reduce into an object</h3>\n<p>The accumulator can be anything, including an object. That is how you build per-category totals:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"1\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"1\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> expenses</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  { label: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Coffee\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, amount: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">450</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, category: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"food\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  { label: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Train\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, amount: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1220</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, category: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"travel\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  { label: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Lunch\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, amount: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1350</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, category: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"food\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">];</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> byCategory</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> expenses.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">reduce</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">totals</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">e</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> {</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  totals[e.category] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> (totals[e.category] </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">??</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> 0</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">+</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> e.amount;</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">  return</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> totals;</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">}, {});</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(byCategory);</span></span></code></pre><div class=\"run-output\" data-run-output=\"1\" hidden></div></div>\n<p>For each expense, read the category’s running total (or 0 if this is the first time the category appears; the <code dir=\"ltr\">??</code> operator gets its own explanation next lesson), add the amount, and store it back with bracket notation, since the property name is in a variable. Then return the same object so the next call receives it.</p>\n<aside class=\"callout callout-mistake\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3\"/><path d=\"M12 9v4\"/><path d=\"M12 17h.01\"/></svg><span><span class=\"callout-kind\">Common mistake</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Forgetting to return the accumulator</span></p>\n<p>With braces in the callback, the last line must be <code dir=\"ltr\">return totals;</code>. Forget it and the next call receives <code dir=\"ltr\">undefined</code> as its accumulator and the callback throws <code dir=\"ltr\">TypeError: Cannot read properties of undefined</code> as soon as it reads <code dir=\"ltr\">totals[e.category]</code>. When <code dir=\"ltr\">reduce</code> misbehaves, check the return first.</p>\n</aside>\n<p>When you want the expenses themselves grouped, rather than a total per group, there is a built-in shortcut since 2024: <code dir=\"ltr\">Object.groupBy(expenses, (e) =&gt; e.category)</code> returns an object whose properties are the categories and whose values are arrays of the matching expenses. It is supported in all current browsers and Node.js, and it reads better than the equivalent <code dir=\"ltr\">reduce</code>. Totals still need <code dir=\"ltr\">reduce</code> (or a loop over the groups).</p>\n<p><code dir=\"ltr\">reduce</code> is powerful, and that is a reason to use it with restraint. If a <code dir=\"ltr\">map</code>, <code dir=\"ltr\">filter</code> or a short <code dir=\"ltr\">for...of</code> says the same thing more plainly, prefer it. Totals and grouping are where <code dir=\"ltr\">reduce</code> reads best.</p>\n<h2 id=\"sec-3\" tabindex=\"-1\">sort: putting things in order</h2>\n<p>Sorting looks easy and contains the most famous trap in JavaScript:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"2\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"2\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> amounts</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">450</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1220</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">99</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">];</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">amounts.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">sort</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">();</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(amounts);</span></span></code></pre><div class=\"run-output\" data-run-output=\"2\" hidden></div></div>\n<p>Without instructions, <code dir=\"ltr\">sort</code> converts every item to a string and orders them as text, like words in a dictionary. As text, “1220” comes before “450” because the character “1” comes before “4”. That is fine for words and wrong for numbers.</p>\n<p>The fix is a <strong>comparator</strong>: a function that receives two items, <code dir=\"ltr\">a</code> and <code dir=\"ltr\">b</code>, and returns a number that says which comes first.</p>\n<div class=\"table-wrap\" role=\"region\" aria-label=\"Scrollable table\" tabindex=\"0\"><table><thead>\n<tr>\n<th>Comparator returns</th>\n<th>Result</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>a negative number</td>\n<td><code dir=\"ltr\">a</code> goes before <code dir=\"ltr\">b</code></td>\n</tr>\n<tr>\n<td>a positive number</td>\n<td><code dir=\"ltr\">b</code> goes before <code dir=\"ltr\">a</code></td>\n</tr>\n<tr>\n<td><code dir=\"ltr\">0</code></td>\n<td>keep their current order</td>\n</tr>\n</tbody>\n</table></div><p>For numbers, subtraction produces exactly that:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"3\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"3\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> amounts</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">450</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1220</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">99</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">];</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">([</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">...</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">amounts].</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">sort</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">b</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> a </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">-</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> b));  </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// ascending</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">([</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">...</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">amounts].</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">sort</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">b</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> b </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">-</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> a));  </span><span style=\"color:#6E7781;--shiki-dark:#8B949E\">// descending</span></span></code></pre><div class=\"run-output\" data-run-output=\"3\" hidden></div></div>\n<p>When <code dir=\"ltr\">a</code> is 450 and <code dir=\"ltr\">b</code> is 99, <code dir=\"ltr\">a - b</code> is positive, so 99 moves first. Flip it to <code dir=\"ltr\">b - a</code> for biggest first.</p>\n<p>Text needs its own comparison, because <code dir=\"ltr\">&lt;</code> compares character codes and puts every uppercase letter before every lowercase one. <code dir=\"ltr\">localeCompare</code> compares the way a dictionary in a real language does:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"4\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"4\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> labels</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"lunch\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Train\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"coffee\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Éclair\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">];</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(labels.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">toSorted</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">b</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> a.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">localeCompare</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(b)));</span></span></code></pre><div class=\"run-output\" data-run-output=\"4\" hidden></div></div>\n<p>Objects are sorted by one of their properties, using the same patterns: <code dir=\"ltr\">(a, b) =&gt; b.amount - a.amount</code> for most expensive first, <code dir=\"ltr\">(a, b) =&gt; a.label.localeCompare(b.label)</code> for alphabetical.</p>\n<h3 id=\"sec-4\" tabindex=\"-1\">sort changes the original</h3>\n<p><code dir=\"ltr\">sort</code> rearranges the array <strong>in place</strong> and returns that same array. If other code is reading the list, perhaps to show expenses in the order they were added, it is now sorted too. That is why the examples above sort a copy, <code dir=\"ltr\">[...amounts]</code>. The newer <code dir=\"ltr\">toSorted</code> method (part of JavaScript since 2023 and supported by all current browsers) returns a sorted copy directly, so you do not have to remember the copy step:</p>\n<div class=\"code-block\" dir=\"ltr\" data-lang=\"js\" data-run=\"js\" data-index=\"5\"><div class=\"code-head\"><span class=\"code-title\">JavaScript</span><span class=\"code-actions\"><button type=\"button\" class=\"code-btn code-run\" data-action=\"run\" data-lang=\"js\" data-index=\"5\" aria-label=\"Run this example\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z\"/></svg><span>Run</span></button><button type=\"button\" class=\"code-btn\" data-action=\"copy\" aria-label=\"Copy code\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\"/><path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\"/></svg><span>Copy</span></button></span></div><pre class=\"shiki\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> expenses</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> [</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  { label: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Coffee\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, amount: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">450</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  { label: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Train\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, amount: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1220</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">  { label: </span><span style=\"color:#0A3069;--shiki-dark:#A5D6FF\">\"Lunch\"</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, amount: </span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\">1350</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> },</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">];</span></span>\n<span class=\"line\"><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">const</span><span style=\"color:#0550AE;--shiki-dark:#79C0FF\"> biggestFirst</span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\"> =</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> expenses.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">toSorted</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">a</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">, </span><span style=\"color:#953800;--shiki-dark:#FFA657\">b</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> b.amount </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">-</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> a.amount);</span></span>\n<span class=\"line\"><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">console.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">log</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">(biggestFirst.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">map</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">e</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> e.label), expenses.</span><span style=\"color:#8250DF;--shiki-dark:#D2A8FF\">map</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">((</span><span style=\"color:#953800;--shiki-dark:#FFA657\">e</span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\">) </span><span style=\"color:#CF222E;--shiki-dark:#FF7B72\">=></span><span style=\"color:#1F2328;--shiki-dark:#E6EDF3\"> e.label));</span></span></code></pre><div class=\"run-output\" data-run-output=\"5\" hidden></div></div>\n<aside class=\"callout callout-tip\" role=\"note\"><p class=\"callout-title\"><svg class=\"icon \" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\" focusable=\"false\"><path d=\"M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5\"/><path d=\"M9 18h6\"/><path d=\"M10 22h4\"/></svg><span><span class=\"callout-kind\">Tip</span><span class=\"callout-sep\" aria-hidden=\"true\"> · </span>Default to non-mutating</span></p>\n<p>In this course’s code, and in most modern codebases, the default is to produce new arrays: <code dir=\"ltr\">map</code>, <code dir=\"ltr\">filter</code>, <code dir=\"ltr\">toSorted</code>, spread. Reach for mutating methods when you own the array and nobody else reads it. It removes a whole category of “who changed my data?” bugs.</p>\n</aside>\n<p>Next you deal with data that is not there at all: missing properties, empty values, and two collections, Map and Set, built for lookups and uniqueness.</p>\n","toc":[{"id":"sec-1","text":"reduce: many values into one","level":2},{"id":"sec-2","text":"Grouping: reduce into an object","level":3},{"id":"sec-3","text":"sort: putting things in order","level":2},{"id":"sec-4","text":"sort changes the original","level":3}],"takeaways":["<code dir=\"ltr\">reduce</code> walks a list carrying an accumulator; the callback returns the next accumulator, and the last one is the result.","Always pass <code dir=\"ltr\">reduce</code> an initial value, such as <code dir=\"ltr\">0</code> for totals or <code dir=\"ltr\">{}</code> for grouping; without one, an empty list throws.","Without a comparator, <code dir=\"ltr\">sort</code> compares items as strings, so <code dir=\"ltr\">[450, 1220, 99]</code> sorts to <code dir=\"ltr\">[1220, 450, 99]</code>.","A comparator returns a negative number to put <code dir=\"ltr\">a</code> first, positive to put <code dir=\"ltr\">b</code> first; <code dir=\"ltr\">(a, b) =&gt; a - b</code> sorts numbers ascending.","<code dir=\"ltr\">sort</code> mutates the array in place; use <code dir=\"ltr\">toSorted</code> (or sort a copy) when the original must stay as it is."],"further":[{"title":"Array.prototype.reduce() (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce"},{"title":"Array.prototype.sort() (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort"},{"title":"Array.prototype.toSorted() (MDN)","url":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toSorted"}],"quiz":[{"q":"<p>What does <code dir=\"ltr\">[450, 1220, 99].sort()</code> return?</p>","options":[{"text":"<code dir=\"ltr\">[99, 450, 1220]</code>","why":"That needs a numeric comparator. The default sort turns each number into a string and compares the text."},{"text":"<code dir=\"ltr\">[1220, 450, 99]</code>","why":"Correct. As strings, “1220” comes before “450” because “1” comes before “4”, and “450” before “99” because “4” comes before “9”."},{"text":"<code dir=\"ltr\">[1220, 99, 450]</code>","why":"Text is compared character by character from the left, so “450” sorts before “99”."}],"answer":1},{"q":"<p>Which comparator sorts expenses from the most expensive to the cheapest?</p>","options":[{"text":"<code dir=\"ltr\">(a, b) =&gt; a.amount - b.amount</code>","why":"This returns a negative number when <code dir=\"ltr\">a</code> is cheaper, putting cheaper items first. That is ascending order."},{"text":"<code dir=\"ltr\">(a, b) =&gt; a.amount &gt; b.amount</code>","why":"This returns a boolean, not a number. Comparators must return negative, zero or positive; booleans give unreliable results."},{"text":"<code dir=\"ltr\">(a, b) =&gt; b.amount - a.amount</code>","why":"Correct. When <code dir=\"ltr\">b</code> costs more, the result is positive, so <code dir=\"ltr\">b</code> is placed before <code dir=\"ltr\">a</code>. Bigger amounts come first."}],"answer":2},{"q":"<p><code dir=\"ltr\">[].reduce((sum, e) =&gt; sum + e.amount)</code> is called on an empty list. What happens?</p>","options":[{"text":"It throws a TypeError, because there is no initial value and no first item to start from.","why":"Correct. Passing an initial value, <code dir=\"ltr\">reduce(fn, 0)</code>, makes it return 0 for an empty list instead."},{"text":"It returns 0.","why":"Only if you pass 0 as the initial value. Without it, <code dir=\"ltr\">reduce</code> has nothing to start the accumulator with."},{"text":"It returns <code dir=\"ltr\">undefined</code>.","why":"<code dir=\"ltr\">reduce</code> does not quietly return <code dir=\"ltr\">undefined</code> here; it throws “Reduce of empty array with no initial value”."}],"answer":0},{"q":"<p>You show expenses sorted by amount in one panel and in the order they were added in another. Both panels read the same <code dir=\"ltr\">expenses</code> array. What goes wrong with <code dir=\"ltr\">expenses.sort(byAmount)</code>?</p>","options":[{"text":"Nothing, because <code dir=\"ltr\">sort</code> returns a sorted copy.","why":"<code dir=\"ltr\">sort</code> returns the same array it sorted. It reorders the original in place."},{"text":"The “order added” panel also becomes sorted, because <code dir=\"ltr\">sort</code> mutated the shared array.","why":"Correct. Use <code dir=\"ltr\">expenses.toSorted(byAmount)</code> or <code dir=\"ltr\">[...expenses].sort(byAmount)</code> to sort a copy."},{"text":"It throws, because the array is declared with <code dir=\"ltr\">const</code>.","why":"<code dir=\"ltr\">const</code> does not prevent changes to the array’s contents, and sorting only rearranges contents."}],"answer":1}],"exercise":{"kind":"web","mode":"js","starter":{"js":"const expenses = [\n  { label: \"coffee\", amount: 450, category: \"food\" },\n  { label: \"Train\", amount: 1220, category: \"travel\" },\n  { label: \"Lunch\", amount: 1350, category: \"food\" },\n  { label: \"Museum\", amount: 99, category: \"fun\" },\n];\n\n// Sum of all amounts (0 for an empty list)\nfunction totalCents(expenses) {\n  return 0;\n}\n\n// An object like { food: 1800, travel: 1220, fun: 99 }\nfunction totalsByCategory(expenses) {\n  return {};\n}\n\n// A NEW array, most expensive first; do not reorder the original\nfunction biggestFirst(expenses) {\n  return expenses.sort();\n}\n\n// A NEW array sorted by label, A to Z, ignoring upper/lower case\nfunction alphabetical(expenses) {\n  return expenses;\n}\n\nconsole.log(totalCents(expenses), totalsByCategory(expenses));\n"},"solution":{"js":"const expenses = [\n  { label: \"coffee\", amount: 450, category: \"food\" },\n  { label: \"Train\", amount: 1220, category: \"travel\" },\n  { label: \"Lunch\", amount: 1350, category: \"food\" },\n  { label: \"Museum\", amount: 99, category: \"fun\" },\n];\n\nfunction totalCents(expenses) {\n  return expenses.reduce((sum, e) => sum + e.amount, 0);\n}\n\nfunction totalsByCategory(expenses) {\n  return expenses.reduce((totals, e) => {\n    totals[e.category] = (totals[e.category] ?? 0) + e.amount;\n    return totals;\n  }, {});\n}\n\nfunction biggestFirst(expenses) {\n  return expenses.toSorted((a, b) => b.amount - a.amount);\n}\n\nfunction alphabetical(expenses) {\n  return expenses.toSorted((a, b) => a.label.localeCompare(b.label));\n}\n\nconsole.log(totalCents(expenses), totalsByCategory(expenses));\n"},"checks":[{"id":"total","test":"return totalCents(expenses) === 3119 && totalCents([]) === 0;\n"},{"id":"by-category","test":"const t = totalsByCategory(expenses);\nreturn t.food === 1800 && t.travel === 1220 && t.fun === 99 && Object.keys(t).length === 3;\n"},{"id":"biggest","test":"const out = biggestFirst(expenses);\nreturn Array.isArray(out) && out.map((e) => e.amount).join() === '1350,1220,450,99';\n"},{"id":"alpha","test":"const out = alphabetical(expenses);\nreturn Array.isArray(out) && out.map((e) => e.label).join() === 'coffee,Lunch,Museum,Train';\n"},{"id":"no-mutation","test":"const list = [{ label: 'b', amount: 1 }, { label: 'a', amount: 2 }];\nconst a = biggestFirst(list);\nconst b = alphabetical(list);\nreturn list[0].label === 'b' && a !== list && b !== list;\n"}],"title":"Totals and rankings","prompt":"<p>Fill in four functions for Pocket’s summary screen:</p>\n<ul>\n<li><code dir=\"ltr\">totalCents(expenses)</code> returns the sum of every <code dir=\"ltr\">amount</code> (and <code dir=\"ltr\">0</code> for an empty list). Use <code dir=\"ltr\">reduce</code>.</li>\n<li><code dir=\"ltr\">totalsByCategory(expenses)</code> returns an object with one property per category holding that category’s total, such as <code dir=\"ltr\">{ food: 1800, travel: 1220, fun: 99 }</code>.</li>\n<li><code dir=\"ltr\">biggestFirst(expenses)</code> returns a <strong>new</strong> array sorted by <code dir=\"ltr\">amount</code>, largest first.</li>\n<li><code dir=\"ltr\">alphabetical(expenses)</code> returns a <strong>new</strong> array sorted by <code dir=\"ltr\">label</code> from A to Z, treating “coffee” and “Coffee” alike.</li>\n</ul>\n<p>Neither sort may reorder the array it receives.</p>\n","hints":["<code dir=\"ltr\">reduce((sum, e) =&gt; sum + e.amount, 0)</code>: the <code dir=\"ltr\">0</code> at the end is the initial value, and it is what makes the empty list work.","For the categories, start from <code dir=\"ltr\">{}</code> and in the callback do <code dir=\"ltr\">totals[e.category] = (totals[e.category] ?? 0) + e.amount; return totals;</code>.","<code dir=\"ltr\">toSorted</code> returns a sorted copy. Largest first is <code dir=\"ltr\">(a, b) =&gt; b.amount - a.amount</code>; by label is <code dir=\"ltr\">(a, b) =&gt; a.label.localeCompare(b.label)</code>."],"explanation":"<p>Both totals are the accumulator pattern written with <code dir=\"ltr\">reduce</code>. The initial value decides the shape of the result: <code dir=\"ltr\">0</code> for a number, <code dir=\"ltr\">{}</code> for an object of totals. In the grouping callback, bracket notation creates each category’s property the first time it appears, and returning <code dir=\"ltr\">totals</code> passes the same object on to the next call. The starter’s <code dir=\"ltr\">expenses.sort()</code> failed twice: it sorted the objects as text (every object becomes the string “[object Object]”) and it reordered the caller’s array. <code dir=\"ltr\">toSorted</code> with a comparator fixes both. <code dir=\"ltr\">localeCompare</code> puts “coffee” before “Lunch”, where <code dir=\"ltr\">&lt;</code> would put every capitalised label first.</p>\n","checkLabels":{"total":"<code dir=\"ltr\">totalCents</code> sums the amounts and returns 0 for an empty list","by-category":"<code dir=\"ltr\">totalsByCategory</code> totals each category","biggest":"<code dir=\"ltr\">biggestFirst</code> sorts by amount, largest first","alpha":"<code dir=\"ltr\">alphabetical</code> sorts by label, ignoring case","no-mutation":"The original array keeps its order"}},"words":776,"readMinutes":4}