
    <rss version="2.0"
         xmlns:atom="http://www.w3.org/2005/Atom"
         xmlns:content="http://purl.org/rss/1.0/modules/content">
      <channel>
        <atom:link href="https://tomeraberba.ch/rss.xml" rel="self" type="application/rss+xml" />
        <title><![CDATA[Tomer Aberbach]]></title>
        <link>https://tomeraberba.ch</link>
        <description><![CDATA[The portfolio website and blog of Tomer Aberbach, a New Jersey based software engineer, composer, and music producer.]]></description>
        <language>en-us</language>
        <copyright><![CDATA[© Tomer Aberbach. All rights reserved.]]></copyright>
        <lastBuildDate>Thu, 12 Mar 2026 00:00:00 GMT</lastBuildDate>
        <ttl>40</ttl>
        
              <item>
                <title><![CDATA[Actual Lines (And GIFs) From Tech Recruiter Emails]]></title>
                <link>https://tomeraberba.ch/actual-lines-from-tech-recruiter-emails</link>
                <category><![CDATA[engineering]]></category><category><![CDATA[recruiting]]></category>
                <content:encoded><![CDATA[<link rel="preload" as="image" href="/assets/recruiter1-dZjI4Pp7.gif"/><link rel="preload" as="image" href="/assets/recruiter2-pfS2mD9c.gif"/><link rel="preload" as="image" href="/assets/recruiter3-C_RlhdwW.gif"/><link rel="preload" as="image" href="/assets/recruiter4-CGO8lTEC.gif"/><link rel="preload" as="image" href="/assets/recruiter5-BzTXcLL7.gif"/><p>Inspired by <a href="https://blog.cjquines.com/post/tech-recruiter-emails" rel="nofollow">CJ Quines’s original blog post</a>, which may or may not be a joke, hard to tell… Anyway, the following are real, I promise!</p><ul><li><p>“Tomer?” (this was the whole email)</p></li><li><p>“Transform an unsexy industry!”</p></li><li><p>“I totally get it <span aria-label="face with tears of joy" role="img">😂</span> these emails are super annoying, right?”</p></li><li><p>“How are you?!”</p></li><li><p>“Another technical recruiter here, trying to gas you and see if you’d like to discuss. Annoyed, non-plussed, or charmed? Lmk for research purposes…”</p></li><li><p>“We also offer a referral bonus – we’ll treat you to any restaurant of your choice if you refer a person that we hire.”</p></li><li><p>“Exactly the muscle and firepower we need! <span aria-label="fire" role="img">🔥</span>”</p></li><li><p>“The engineering team remains embryonic.”</p></li><li><p>“Your FP iterator library (lfi) shows the design taste we need for shopping AI that feels human.”</p></li><li><p>“Me again <span aria-label="slightly smiling face" role="img">🙂</span>”</p></li><li><p>“I came across your details via some of your impressive open source projects like eslint-config.”</p></li><li><p>“A career is like a frog in boiling water.”</p></li><li><p>“Quick question: What’s the difference between debugging code and my follow-up game? One actually gets results <span aria-label="grinning face with sweat" role="img">😅</span>”</p></li><li><p>“With this software, there’s no way in hell Ferris Bueller would’ve had so many days off.”</p></li><li><p>“Yo yo yo!!”</p></li><li><p>“Dropping some more heat <span aria-label="fire" role="img">🔥</span>…”</p></li><li><p>“Trust me I don’t want to be a pest here, Tomer, but I’d be silly if I didn’t insist on getting your attention.”</p></li><li><p>“This could be a <strong>legacy</strong> building role.” (yes, in bold)</p></li><li><p><img alt="Woody stepping out of box gif" src="/assets/recruiter1-dZjI4Pp7.gif"/></p></li><li><p><img alt="Obi Wan &quot;hello there&quot; gif" src="/assets/recruiter2-pfS2mD9c.gif"/></p></li><li><p><img alt="Sad Charmander gif" src="/assets/recruiter3-C_RlhdwW.gif"/></p></li><li><p><img alt="Tom Hanks Thanks gif" src="/assets/recruiter4-CGO8lTEC.gif"/></p></li><li><p>“P.S. Here’s another attempt to make my emails more enjoyable: The glass is your inbox. The manatee is my email. Together they create the aforementioned bump!”</p><p><img alt="Manatee boop gif" src="/assets/recruiter5-BzTXcLL7.gif"/></p></li></ul>]]></content:encoded>
                <description><![CDATA[Inspired by CJ Quines's original blog post, which may or may not be a joke, hard to tell... Anyway, the following are real, I promise! "Tomer?" (this was the whole email) "Transform an unsexy…]]></description>
                <guid isPermaLink="true">https://tomeraberba.ch/actual-lines-from-tech-recruiter-emails</guid>
                <pubDate>Sat, 13 Dec 2025 00:00:00 GMT</pubDate>
                <lastBuildDate>Thu, 12 Mar 2026 00:00:00 GMT</lastBuildDate>
              </item>
            
              <item>
                <title><![CDATA[Cursed Knowledge]]></title>
                <link>https://tomeraberba.ch/cursed-knowledge</link>
                <category><![CDATA[bugs]]></category><category><![CDATA[c#]]></category><category><![CDATA[code]]></category><category><![CDATA[dates]]></category><category><![CDATA[engineering]]></category><category><![CDATA[java]]></category><category><![CDATA[javascript]]></category><category><![CDATA[learning]]></category><category><![CDATA[performance]]></category><category><![CDATA[php]]></category><category><![CDATA[python]]></category><category><![CDATA[ruby]]></category><category><![CDATA[typescript]]></category>
                <content:encoded><![CDATA[<p>Cursed knowledge I have learned over time that I wish I never knew. Inspired by <a href="https://immich.app/cursed-knowledge" rel="nofollow">Immich’s Cursed Knowledge</a>. The knowledge is ordered from most to least recently learned.</p><details><summary><h2 id="ajv-multipleof-validation-is-cursed" class="group relative"><a data-discover="true" href="#ajv-multipleof-validation-is-cursed" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Ajv &lt;code&gt;multipleOf&lt;/code&gt; validation is cursed permalink"/></a>Ajv <code>multipleOf</code> validation is cursed</h2></summary><p><a href="https://github.com/ajv-validator" rel="nofollow">Ajv</a> validates a number against <a href="https://json-schema.org/understanding-json-schema/reference/numeric#multiples" rel="nofollow"><code>multipleOf</code></a> like so:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> quotient </span><span style="color:#89ddff">=</span><span style="color:#babed8"> input </span><span style="color:#89ddff">/</span><span style="color:#babed8"> multipleOf</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> isMultipleOf </span><span style="color:#89ddff">=</span><span style="color:#babed8"> quotient </span><span style="color:#89ddff">===</span><span style="color:#82aaff"> parseInt</span><span style="color:#babed8">(</span><span style="color:#82aaff">String</span><span style="color:#babed8">(quotient))</span></span></code></pre><p>This doesn’t work for <a href="https://github.com/ajv-validator/ajv/issues/2561" rel="nofollow">large numbers like <code>1e21</code></a>, even for <code>multipleOf: 1</code>, because <code>parseInt(&quot;1e21&quot;)</code> is <code>1</code>, not <code>1e21</code>.</p><p>This is cursed because the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Remainder" rel="nofollow">JavaScript remainder operator</a> is right there! <span aria-label="person facepalming" role="img">🤦</span></p></details><details><summary><h2 id="typescript-template-literal-types-are-cursed" class="group relative"><a data-discover="true" href="#typescript-template-literal-types-are-cursed" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="TypeScript template literal types are cursed permalink"/></a>TypeScript template literal types are cursed</h2></summary><p>Replacing a template literal type’s interpolated types with subtypes doesn’t necessarily result in a subtype of the original template literal type.</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> first</span><span style="color:#89ddff">:</span><span style="color:#ffcb6b"> string</span><span style="color:#89ddff"> =</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d"> -</span><span style="color:#89ddff">&#x27;</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> second</span><span style="color:#89ddff">:</span><span style="color:#ffcb6b"> number</span><span style="color:#89ddff"> =</span><span style="color:#f78c6c"> 0</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> third</span><span style="color:#89ddff">:</span><span style="color:#ffcb6b"> string</span><span style="color:#89ddff"> =</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">a</span><span style="color:#89ddff">&#x27;</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">// This typechecks :)</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> a</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> `${</span><span style="color:#ffcb6b">string</span><span style="color:#89ddff">}</span><span style="color:#c3e88d"> - </span><span style="color:#89ddff">${</span><span style="color:#ffcb6b">number</span><span style="color:#89ddff">}</span><span style="color:#c3e88d"> - </span><span style="color:#89ddff">${</span><span style="color:#ffcb6b">string</span><span style="color:#89ddff">}`</span><span style="color:#89ddff"> =</span><span style="color:#89ddff"> `${</span><span style="color:#babed8">first</span><span style="color:#89ddff">}</span><span style="color:#c3e88d"> - </span><span style="color:#89ddff">${</span><span style="color:#babed8">second</span><span style="color:#89ddff">}</span><span style="color:#c3e88d"> - </span><span style="color:#89ddff">${</span><span style="color:#babed8">third</span><span style="color:#89ddff">}`</span></span>
<span class="line"></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> first1 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d"> -</span><span style="color:#89ddff">&#x27;</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> second1 </span><span style="color:#89ddff">=</span><span style="color:#f78c6c"> 0</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> third1 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">a</span><span style="color:#89ddff">&#x27;</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">// None of these typecheck :(</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">// Even though `first1`, `second1`, and `third1` are subtypes of `first`, `second`, and `third`, respectively</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> b</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> `${</span><span style="color:#ffcb6b">string</span><span style="color:#89ddff">}</span><span style="color:#c3e88d"> - </span><span style="color:#89ddff">${</span><span style="color:#ffcb6b">number</span><span style="color:#89ddff">}</span><span style="color:#c3e88d"> - </span><span style="color:#89ddff">${</span><span style="color:#ffcb6b">string</span><span style="color:#89ddff">}`</span><span style="color:#89ddff"> =</span><span style="color:#89ddff"> `${</span><span style="color:#babed8">first1</span><span style="color:#89ddff">}</span><span style="color:#c3e88d"> - </span><span style="color:#89ddff">${</span><span style="color:#babed8">second1</span><span style="color:#89ddff">}</span><span style="color:#c3e88d"> - </span><span style="color:#89ddff">${</span><span style="color:#babed8">third1</span><span style="color:#89ddff">}`</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> c</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> `${</span><span style="color:#ffcb6b">string</span><span style="color:#89ddff">}</span><span style="color:#c3e88d"> - </span><span style="color:#89ddff">${</span><span style="color:#ffcb6b">number</span><span style="color:#89ddff">}</span><span style="color:#c3e88d"> - </span><span style="color:#89ddff">${</span><span style="color:#ffcb6b">string</span><span style="color:#89ddff">}`</span><span style="color:#89ddff"> =</span><span style="color:#89ddff"> `${&#x27;</span><span style="color:#c3e88d"> -</span><span style="color:#89ddff">&#x27;}</span><span style="color:#c3e88d"> - </span><span style="color:#89ddff">${</span><span style="color:#f78c6c">0</span><span style="color:#89ddff">}</span><span style="color:#c3e88d"> - </span><span style="color:#89ddff">${&#x27;</span><span style="color:#c3e88d">a</span><span style="color:#89ddff">&#x27;}`</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> d</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> `${</span><span style="color:#ffcb6b">string</span><span style="color:#89ddff">}</span><span style="color:#c3e88d"> - </span><span style="color:#89ddff">${</span><span style="color:#ffcb6b">number</span><span style="color:#89ddff">}</span><span style="color:#c3e88d"> - </span><span style="color:#89ddff">${</span><span style="color:#ffcb6b">string</span><span style="color:#89ddff">}`</span><span style="color:#89ddff"> =</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d"> - - 0 - x</span><span style="color:#89ddff">&#x27;</span></span></code></pre><p>This is cursed because the behavior violates the <a href="https://en.wikipedia.org/wiki/Liskov_substitution_principle" rel="nofollow">Liskov substitution principle</a>. Unfortunately, <a href="https://github.com/microsoft/TypeScript/issues/62625" rel="nofollow">it’s unlikely to be fixed</a>.</p></details><details><summary><h2 id="php-variables-are-cursed" class="group relative"><a data-discover="true" href="#php-variables-are-cursed" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="PHP variables are cursed permalink"/></a>PHP variables are cursed</h2></summary><p>PHP’s <code>$</code> symbol is an operator that looks up a variable by name.</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#89ddff">$</span><span style="color:#babed8">foo </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> &quot;</span><span style="color:#c3e88d">hi</span><span style="color:#89ddff">&quot;</span><span style="color:#89ddff">;</span></span>
<span class="line"><span style="color:#89ddff">$</span><span style="color:#babed8">bar </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> &quot;</span><span style="color:#c3e88d">foo</span><span style="color:#89ddff">&quot;</span><span style="color:#89ddff">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#82aaff">echo</span><span style="color:#89ddff"> $$</span><span style="color:#babed8">bar</span><span style="color:#89ddff">;</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; hi</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89ddff">$</span><span style="color:#babed8">a </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> &quot;</span><span style="color:#c3e88d">hi</span><span style="color:#89ddff">&quot;</span><span style="color:#89ddff">;</span></span>
<span class="line"><span style="color:#89ddff">$</span><span style="color:#babed8">b </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> &quot;</span><span style="color:#c3e88d">a</span><span style="color:#89ddff">&quot;</span><span style="color:#89ddff">;</span></span>
<span class="line"><span style="color:#89ddff">$</span><span style="color:#babed8">c </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> &quot;</span><span style="color:#c3e88d">b</span><span style="color:#89ddff">&quot;</span><span style="color:#89ddff">;</span></span>
<span class="line"><span style="color:#89ddff">$</span><span style="color:#babed8">d </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> &quot;</span><span style="color:#c3e88d">c</span><span style="color:#89ddff">&quot;</span><span style="color:#89ddff">;</span></span>
<span class="line"><span style="color:#89ddff">$</span><span style="color:#babed8">e </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> &quot;</span><span style="color:#c3e88d">d</span><span style="color:#89ddff">&quot;</span><span style="color:#89ddff">;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#82aaff">echo</span><span style="color:#89ddff"> $$$$$</span><span style="color:#babed8">e</span><span style="color:#89ddff">;</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; hi</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89ddff">$$$$$</span><span style="color:#babed8">e </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> &quot;</span><span style="color:#c3e88d">bye</span><span style="color:#89ddff">&quot;</span><span style="color:#89ddff">;</span></span>
<span class="line"><span style="color:#82aaff">echo</span><span style="color:#89ddff"> $</span><span style="color:#babed8">a</span><span style="color:#89ddff">;</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; bye</span></span></code></pre><p>This is cursed because it’s an unnecessary feature that enables writing incomprehensible code.</p><p>Credit goes to <a href="https://www.scd31.com" rel="nofollow">Stephen Downward</a> for telling me about it!</p></details><details><summary><h2 id="github-actionss-sleep-command-is-cursed" class="group relative"><a data-discover="true" href="#github-actionss-sleep-command-is-cursed" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="GitHub Actions’s &lt;code&gt;sleep&lt;/code&gt; command is cursed permalink"/></a>GitHub Actions’s <code>sleep</code> command is cursed</h2></summary><p>GitHub Actions’s <a href="https://github.com/actions/runner/blob/main/src/Misc/layoutroot/safe_sleep.sh" rel="nofollow"><code>sleep</code> command</a> is implemented as a <a href="https://en.wikipedia.org/wiki/Busy_waiting" rel="nofollow">busy wait</a>.</p><p>This is cursed because it can result in <a href="https://github.com/actions/runner/issues/2380" rel="nofollow">100% CPU usage</a> or even <a href="https://github.com/actions/runner/issues/3792" rel="nofollow">looping forever</a>, but sleeping is supposed to allow other tasks to run.</p><p>Credit goes to <a href="https://github.com/ms-jpq" rel="nofollow">Hao Wang</a> for telling me about it!</p><p>UPDATE: <a href="https://github.com/actions/runner/issues/3792#issuecomment-3597495291" rel="nofollow">Looping forever is now fixed</a>.</p></details><details><summary><h2 id="css-margin-collapse-is-cursed" class="group relative"><a data-discover="true" href="#css-margin-collapse-is-cursed" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="CSS margin collapse is cursed permalink"/></a>CSS margin collapse is cursed</h2></summary><p>CSS margins <a href="https://www.joshwcomeau.com/css/rules-of-margin-collapse#only-vertical-margins-collapse-1" rel="nofollow">collapse vertically, but not horizontally</a>.</p><p>This is cursed because it’s weirdly inconsistent and makes the rules of margin collapse even more confusing than they already are.</p><p>Credit goes to <a href="https://fostersamuel.com" rel="nofollow">Samuel Foster</a> for telling me about it!</p></details><details><summary><h2 id="c-jsonelements-tryget-methods-are-cursed" class="group relative"><a data-discover="true" href="#c-jsonelements-tryget-methods-are-cursed" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="C# &lt;code&gt;JsonElement&lt;/code&gt;’s &lt;code&gt;TryGet&lt;/code&gt; methods are cursed permalink"/></a>C# <code>JsonElement</code>’s <code>TryGet</code> methods are cursed</h2></summary><p><a href="https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonelement#methods" rel="nofollow"><code>JsonElement</code></a> has <code>GetByte</code>/<code>TryGetByte</code>, <code>GetDateTime</code>/<code>TryGetDateTime</code>, <code>GetDouble</code>/<code>TryGetDouble</code>, etc. However, <code>GetBoolean</code> and <code>GetString</code> have no corresponding <code>TryGet</code> methods. What gives?</p><p>You’d expect <code>Get</code> methods to throw exceptions and <code>TryGet</code> methods to gracefully handle type mismatches, but that’s only half true. For example, the <code>TryGetByte</code> method:</p><ol><li>Returns <code>true</code> for JSON numbers that fit in a <a href="https://learn.microsoft.com/en-us/dotnet/api/system.byte" rel="nofollow"><code>Byte</code></a></li><li>Returns <code>false</code> for JSON numbers that <em>don’t</em> fit in a <code>Byte</code></li><li>Throws an exception for non-number JSON values (e.g. arrays and strings)</li></ol><p>The <code>TryGet</code> methods are only graceful <em>after</em> confirming the <a href="https://learn.microsoft.com/en-us/dotnet/api/system.text.json.jsonvaluekind" rel="nofollow"><code>JsonValueKind</code></a> matches. This means that <code>TryGetBoolean</code> and <code>TryGetString</code> would behave identically to <code>GetBoolean</code> and <code>GetString</code>, respectively, because they have nothing to validate after the <code>JsonValueKind</code>. The methods don’t exist because they’d be pointless.</p><p>This is cursed because the <code>TryGet</code> method names promise graceful error handling, but the methods still throw exceptions. It’s a misleading API that provides no real benefit over wrapping a <code>Get</code> method in a <code>try-catch</code>.</p></details><details><summary><h2 id="typescript-readonly-properties-are-cursed" class="group relative"><a data-discover="true" href="#typescript-readonly-properties-are-cursed" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="TypeScript &lt;code&gt;readonly&lt;/code&gt; properties are cursed permalink"/></a>TypeScript <code>readonly</code> properties are cursed</h2></summary><p><a href="https://www.typescriptlang.org/docs/handbook/2/objects.html#readonly-properties" rel="nofollow">Marking a property as <code>readonly</code></a> tells TypeScript to disallow writing to it during type-checking.</p><p>However, it’s a meaningless guardrail because <a href="https://github.com/microsoft/TypeScript/issues/13347" rel="nofollow">TypeScript allows assigning a type with a <code>readonly</code> property to a type with a writable property</a>.</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">type</span><span style="color:#ffcb6b"> Person</span><span style="color:#89ddff"> =</span><span style="color:#89ddff"> {</span></span>
<span class="line"><span style="color:#f07178">  name</span><span style="color:#89ddff">:</span><span style="color:#ffcb6b"> string</span></span>
<span class="line"><span style="color:#89ddff">}</span></span>
<span class="line"><span style="color:#c792ea">type</span><span style="color:#ffcb6b"> ReadonlyPerson</span><span style="color:#89ddff"> =</span><span style="color:#89ddff"> {</span></span>
<span class="line"><span style="color:#c792ea">  readonly</span><span style="color:#f07178"> name</span><span style="color:#89ddff">:</span><span style="color:#ffcb6b"> string</span></span>
<span class="line"><span style="color:#89ddff">}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> readonlyPerson</span><span style="color:#89ddff">:</span><span style="color:#ffcb6b"> ReadonlyPerson</span><span style="color:#89ddff"> =</span><span style="color:#89ddff"> {</span><span style="color:#f07178"> name</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Tomer</span><span style="color:#89ddff">`</span><span style="color:#89ddff"> }</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">// Cannot assign to &#x27;name&#x27; because it is a read-only property.</span></span>
<span class="line"><span style="color:#babed8">readonlyPerson</span><span style="color:#89ddff">.</span><span style="color:#babed8">name </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Tumor</span><span style="color:#89ddff">`</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676e95;font-style:italic">// Typechecks! 😱</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> writablePerson</span><span style="color:#89ddff">:</span><span style="color:#ffcb6b"> Person</span><span style="color:#89ddff"> =</span><span style="color:#babed8"> readonlyPerson</span></span>
<span class="line"><span style="color:#babed8">writablePerson</span><span style="color:#89ddff">.</span><span style="color:#babed8">name </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Tumor</span><span style="color:#89ddff">`</span></span></code></pre><p>This is cursed because <code>readonly</code> gives developers a false sense of security while being trivial to bypass, even by accident.</p><p>Luckily there’s an <a href="https://github.com/microsoft/TypeScript/pull/58296" rel="nofollow">open PR that adds a flag for enforcing <code>readonly</code> properties</a>.</p></details><details><summary><h2 id="maven-dependency-mediation-is-cursed" class="group relative"><a data-discover="true" href="#maven-dependency-mediation-is-cursed" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Maven dependency mediation is cursed permalink"/></a>Maven dependency mediation is cursed</h2></summary><p>When multiples versions of a dependency appear in the dependency tree, Maven <a href="https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Transitive_Dependencies" rel="nofollow">chooses the version closest to the project root</a>; not the highest version.</p><p>This is cursed because it leads to unpredictable dependency resolution that silently downgrades transitive dependencies.</p><p>This behavior even caused <a href="https://www.stainless.com/blog/escaping-maven-dependency-hell" rel="nofollow">a bug in the OpenAI Java SDK</a>!</p></details><details><summary><h2 id="rubocop-is-cursed" class="group relative"><a data-discover="true" href="#rubocop-is-cursed" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="RuboCop is cursed permalink"/></a>RuboCop is cursed</h2></summary><p><a href="https://docs.rubocop.org" rel="nofollow">RuboCop</a>, a popular Ruby formatter and linter, has auto-fixable lint rules known as <a href="https://docs.rubocop.org/rubocop/cops.html" rel="nofollow">“cops”</a>. Every time a cop fixes a problem in a file, <a href="https://github.com/rubocop/rubocop/issues/6492#issuecomment-439306272" rel="nofollow">every other cop reruns on that file</a>.</p><p>This is cursed because it takes <a href="https://github.com/rubocop/rubocop/issues/2280" rel="nofollow">infinite time</a> to run in the worst case.</p><p>Credit goes to <a href="https://github.com/ms-jpq" rel="nofollow">Hao Wang</a> for telling me about it!</p></details><details><summary><h2 id="javascript-dates-setmonth-method-is-cursed" class="group relative"><a data-discover="true" href="#javascript-dates-setmonth-method-is-cursed" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="JavaScript &lt;code&gt;Date&lt;/code&gt;’s &lt;code&gt;setMonth&lt;/code&gt; method is cursed permalink"/></a>JavaScript <code>Date</code>’s <code>setMonth</code> method is cursed</h2></summary><p>Calling <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMonth" rel="nofollow"><code>setMonth(month)</code></a> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMonth#description" rel="nofollow">doesn’t always update the date to the given <code>month</code></a>. For example, if the date is August 31, then setting the month to September will update the date to October 1. September only has 30 days, so the 31st day “overflows” to the next month.</p><p>This is cursed because it violates the fundamental expectation that calling a setter method with a value actually sets that value.</p><p>This behavior even caused <a href="/the-29-days-per-year-bug">a bug in Google Docs</a>!</p></details><details><summary><h2 id="python-default-parameter-values-are-cursed" class="group relative"><a data-discover="true" href="#python-default-parameter-values-are-cursed" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Python default parameter values are cursed permalink"/></a>Python default parameter values are cursed</h2></summary><p>A function’s <a href="https://docs.python.org/3.13/reference/compound_stmts.html#:~:text=Default%20parameter%20values%20are%20evaluated%20from%20left%20to%20right%20when%20the%20function%20definition%20is%20executed" rel="nofollow">default parameter values are evaluated <em>once</em></a>; not on each function call.</p><p>This means you shouldn’t use mutable values for a parameter’s default value:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">def</span><span style="color:#82aaff"> append_fun</span><span style="color:#89ddff">(</span><span style="color:#babed8;font-style:italic">list</span><span style="color:#89ddff">=[]):</span></span>
<span class="line"><span style="color:#ffcb6b">    list</span><span style="color:#89ddff">.</span><span style="color:#82aaff">append</span><span style="color:#89ddff">(</span><span style="color:#89ddff">&#x27;</span><span style="color:#c3e88d">fun</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff">)</span></span>
<span class="line"><span style="color:#89ddff;font-style:italic">    return</span><span style="color:#ffcb6b"> list</span></span>
<span class="line"></span>
<span class="line"><span style="color:#82aaff">print</span><span style="color:#89ddff">(</span><span style="color:#82aaff">append_fun</span><span style="color:#89ddff">())</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">#=&gt; [&#x27;fun&#x27;]</span></span>
<span class="line"><span style="color:#82aaff">print</span><span style="color:#89ddff">(</span><span style="color:#82aaff">append_fun</span><span style="color:#89ddff">())</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">#=&gt; [&#x27;fun&#x27;, &#x27;fun&#x27;]</span></span>
<span class="line"><span style="color:#82aaff">print</span><span style="color:#89ddff">(</span><span style="color:#82aaff">append_fun</span><span style="color:#89ddff">())</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">#=&gt; [&#x27;fun&#x27;, &#x27;fun&#x27;, &#x27;fun&#x27;]</span></span></code></pre><p>You have to apply the default in the function body instead:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">def</span><span style="color:#82aaff"> append_fun</span><span style="color:#89ddff">(</span><span style="color:#babed8;font-style:italic">list</span><span style="color:#89ddff">=None):</span></span>
<span class="line"><span style="color:#89ddff;font-style:italic">    if</span><span style="color:#ffcb6b"> list</span><span style="color:#89ddff"> is</span><span style="color:#89ddff"> None:</span></span>
<span class="line"><span style="color:#ffcb6b">        list</span><span style="color:#89ddff"> =</span><span style="color:#89ddff"> []</span></span>
<span class="line"><span style="color:#ffcb6b">    list</span><span style="color:#89ddff">.</span><span style="color:#82aaff">append</span><span style="color:#89ddff">(</span><span style="color:#89ddff">&#x27;</span><span style="color:#c3e88d">fun</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff">)</span></span>
<span class="line"><span style="color:#89ddff;font-style:italic">    return</span><span style="color:#ffcb6b"> list</span></span>
<span class="line"></span>
<span class="line"><span style="color:#82aaff">print</span><span style="color:#89ddff">(</span><span style="color:#82aaff">append_fun</span><span style="color:#89ddff">())</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">#=&gt; [&#x27;fun&#x27;]</span></span>
<span class="line"><span style="color:#82aaff">print</span><span style="color:#89ddff">(</span><span style="color:#82aaff">append_fun</span><span style="color:#89ddff">())</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">#=&gt; [&#x27;fun&#x27;]</span></span>
<span class="line"><span style="color:#82aaff">print</span><span style="color:#89ddff">(</span><span style="color:#82aaff">append_fun</span><span style="color:#89ddff">())</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">#=&gt; [&#x27;fun&#x27;]</span></span></code></pre><p>This is cursed because it creates invisible shared state between function calls, turning what appears to be a pure function into something stateful.</p></details><details><summary><h2 id="java-urls-identity-methods-are-cursed" class="group relative"><a data-discover="true" href="#java-urls-identity-methods-are-cursed" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Java &lt;code&gt;URL&lt;/code&gt;’s identity methods are cursed permalink"/></a>Java <code>URL</code>’s identity methods are cursed</h2></summary><p>A call to <a href="https://docs.oracle.com/javase/8/docs/api/java/net/URL.html#equals-java.lang.Object-" rel="nofollow"><code>equals</code></a> or <a href="https://docs.oracle.com/javase/8/docs/api/java/net/URL.html#hashCode--" rel="nofollow"><code>hashCode</code></a> may perform a blocking DNS lookup so that two URLs corresponding to the same IP address are considered equal.</p><p>This also means that using <code>URL</code> objects as <a href="https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html" rel="nofollow"><code>HashMap</code></a> keys will result in many DNS lookups.</p><p>This is cursed because identity methods are supposed to be stateless and performant.</p></details>]]></content:encoded>
                <description><![CDATA[Cursed knowledge I have learned over time that I wish I never knew. Inspired by Immich's Cursed Knowledge. The knowledge is ordered from most to least recently learned. \> Ajv multipleOf validation is…]]></description>
                <guid isPermaLink="true">https://tomeraberba.ch/cursed-knowledge</guid>
                <pubDate>Mon, 25 Aug 2025 00:00:00 GMT</pubDate>
                <lastBuildDate>Wed, 03 Dec 2025 00:00:00 GMT</lastBuildDate>
              </item>
            
              <item>
                <title><![CDATA[Schoenberg: The MIDI Esoteric Programming Language]]></title>
                <link>https://tomeraberba.ch/schoenberg</link>
                <category><![CDATA[code]]></category><category><![CDATA[music]]></category><category><![CDATA[programming languages]]></category><category><![CDATA[rust]]></category>
                <content:encoded><![CDATA[<p>Schoenberg is an <a href="https://en.wikipedia.org/wiki/Esoteric_programming_language" rel="nofollow">esoteric programming language</a> where programs are written as <a href="https://en.wikipedia.org/wiki/MIDI" rel="nofollow">MIDI</a> files. A MIDI file is basically digital sheet music that tells a computer which notes to play when and how loudly.</p><p>The programming language interpreter, and <a href="https://en.wikipedia.org/wiki/Source-to-source_compiler" rel="nofollow">transpilers</a> between Schoenberg and <a href="https://esolangs.org/wiki/Brainfuck" rel="nofollow">brainfuck</a> programs, can be found <a href="https://github.com/TomerAberbach/schoenberg" rel="nofollow">on GitHub</a>.</p><h2 id="semantics" class="group relative"><a data-discover="true" href="#semantics" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Semantics permalink"/></a>Semantics</h2><p>The <a href="https://en.wikipedia.org/wiki/Operational_semantics" rel="nofollow">operational semantics</a> of a Schoenberg program directly match those of a brainfuck program. Since brainfuck is <a href="https://en.wikipedia.org/wiki/Turing_completeness" rel="nofollow">Turing-complete</a>, so is Schoenberg.</p><p>Like brainfuck, Schoenberg operates on an array of memory cells, each initialized to zero. There is a pointer, initially pointing to the first memory cell, and there are commands for moving the pointer, modifying the current cell, outputting the current cell, and looping.</p><h2 id="syntax" class="group relative"><a data-discover="true" href="#syntax" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Syntax permalink"/></a>Syntax</h2><p>Since Schoenberg programs are MIDI files, its <a href="https://en.wikipedia.org/wiki/Syntax_(programming_languages)" rel="nofollow">syntax</a> is not text-based.</p><p>Instead, commands are controlled by <a href="https://en.wikipedia.org/wiki/Pitch_class" rel="nofollow">pitch class</a> distance, <a href="https://en.wikipedia.org/wiki/MIDI#Messages" rel="nofollow">velocity</a>, and overlap. <a href="https://en.wikipedia.org/wiki/Rhythm" rel="nofollow">Rhythm</a> has no effect on a program’s behavior unless it causes notes to overlap.</p><div class="overflow-auto"><table><thead><tr><th>Command</th><th>Description</th><th>Syntax</th><th><code>amount</code></th><th>Brainfuck</th></tr></thead><tbody><tr><td>Decrement</td><td>Decrement the pointer cell by <code>amount</code></td><td>Play a note 1 pitch class away from the current note</td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo fence="true">⌈</mo><mfrac><mrow><mtext>vel</mtext><mo>+</mo><mn>1</mn></mrow><mn>32</mn></mfrac><mo fence="true">⌉</mo></mrow><annotation encoding="application/x-tex">\left\lceil \frac{\text{vel} + 1}{32} \right\rceil</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2301em;vertical-align:-.35em"></span><span class="minner"><span class="delimcenter mopen" style="top:0"><span class="delimsizing size1">⌈</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:.8801em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="mtight reset-size6 size3 sizing"><span class="mtight mord"><span class="mtight mord">32</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:.04em"></span></span><span style="top:-3.394em"><span class="pstrut" style="height:3em"></span><span class="mtight reset-size6 size3 sizing"><span class="mtight mord"><span class="mtight mord text"><span class="mtight mord">vel</span></span><span class="mtight mbin">+</span><span class="mtight mord">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:.345em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="delimcenter mclose" style="top:0"><span class="delimsizing size1">⌉</span></span></span></span></span></span></td><td><code>-</code></td></tr><tr><td>Increment</td><td>Increment the pointer cell by <code>amount</code></td><td>Play a note 2 pitch classes away from the current note</td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo fence="true">⌈</mo><mfrac><mrow><mtext>vel</mtext><mo>+</mo><mn>1</mn></mrow><mn>32</mn></mfrac><mo fence="true">⌉</mo></mrow><annotation encoding="application/x-tex">\left\lceil \frac{\text{vel} + 1}{32} \right\rceil</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2301em;vertical-align:-.35em"></span><span class="minner"><span class="delimcenter mopen" style="top:0"><span class="delimsizing size1">⌈</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:.8801em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="mtight reset-size6 size3 sizing"><span class="mtight mord"><span class="mtight mord">32</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:.04em"></span></span><span style="top:-3.394em"><span class="pstrut" style="height:3em"></span><span class="mtight reset-size6 size3 sizing"><span class="mtight mord"><span class="mtight mord text"><span class="mtight mord">vel</span></span><span class="mtight mbin">+</span><span class="mtight mord">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:.345em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="delimcenter mclose" style="top:0"><span class="delimsizing size1">⌉</span></span></span></span></span></span></td><td><code>+</code></td></tr><tr><td>Move left</td><td>Move the pointer left <code>amount</code> times</td><td>Play a note 3 pitch classes away from the current note</td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo fence="true">⌈</mo><mfrac><mrow><mtext>vel</mtext><mo>+</mo><mn>1</mn></mrow><mn>64</mn></mfrac><mo fence="true">⌉</mo></mrow><annotation encoding="application/x-tex">\left\lceil \frac{\text{vel} + 1}{64} \right\rceil</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2301em;vertical-align:-.35em"></span><span class="minner"><span class="delimcenter mopen" style="top:0"><span class="delimsizing size1">⌈</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:.8801em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="mtight reset-size6 size3 sizing"><span class="mtight mord"><span class="mtight mord">64</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:.04em"></span></span><span style="top:-3.394em"><span class="pstrut" style="height:3em"></span><span class="mtight reset-size6 size3 sizing"><span class="mtight mord"><span class="mtight mord text"><span class="mtight mord">vel</span></span><span class="mtight mbin">+</span><span class="mtight mord">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:.345em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="delimcenter mclose" style="top:0"><span class="delimsizing size1">⌉</span></span></span></span></span></span></td><td><code>&lt;</code></td></tr><tr><td>Move right</td><td>Move the pointer right <code>amount</code> times</td><td>Play a note 4 pitch classes away from the current note</td><td><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mo fence="true">⌈</mo><mfrac><mrow><mtext>vel</mtext><mo>+</mo><mn>1</mn></mrow><mn>64</mn></mfrac><mo fence="true">⌉</mo></mrow><annotation encoding="application/x-tex">\left\lceil \frac{\text{vel} + 1}{64} \right\rceil</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.2301em;vertical-align:-.35em"></span><span class="minner"><span class="delimcenter mopen" style="top:0"><span class="delimsizing size1">⌈</span></span><span class="mord"><span class="mopen nulldelimiter"></span><span class="mfrac"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:.8801em"><span style="top:-2.655em"><span class="pstrut" style="height:3em"></span><span class="mtight reset-size6 size3 sizing"><span class="mtight mord"><span class="mtight mord">64</span></span></span></span><span style="top:-3.23em"><span class="pstrut" style="height:3em"></span><span class="frac-line" style="border-bottom-width:.04em"></span></span><span style="top:-3.394em"><span class="pstrut" style="height:3em"></span><span class="mtight reset-size6 size3 sizing"><span class="mtight mord"><span class="mtight mord text"><span class="mtight mord">vel</span></span><span class="mtight mbin">+</span><span class="mtight mord">1</span></span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:.345em"><span></span></span></span></span></span><span class="mclose nulldelimiter"></span></span><span class="delimcenter mclose" style="top:0"><span class="delimsizing size1">⌉</span></span></span></span></span></span></td><td><code>&gt;</code></td></tr><tr><td>Output</td><td>Output the pointer cell</td><td>Play a note 5 pitch classes away from the current note</td><td>N/A</td><td><code>.</code></td></tr><tr><td>Input</td><td>Input a byte into the pointer cell</td><td>Play a note 6 pitch classes away from the current note</td><td>N/A</td><td><code>,</code></td></tr><tr><td>Loop start</td><td>Jump past the matching loop end if the pointer cell is 0</td><td>Play a note that overlaps the current note (a “loop note”)</td><td>N/A</td><td><code>[</code></td></tr><tr><td>Loop end</td><td>Jump back to the matching loop start if the pointer cell is <em>not</em> 0</td><td>Stop playing an active loop note</td><td>N/A</td><td><code>]</code></td></tr></tbody></table></div><p>Additionally:</p><ul><li>Playing the same note twice in a row, corresponding to a pitch class distance of 0, is a no-op. However, playing and overlapping two different notes with the same pitch class (e.g. in different octaves) can be used to start loops without adding other commands.</li><li>When playing multiple notes at exactly the same time (e.g. a <a href="https://en.wikipedia.org/wiki/Chord_(music)" rel="nofollow">chord</a>), the chronology of the notes is considered to be the ascending pitch order.</li></ul><p>Every MIDI file is a syntactically valid Schoenberg program, although most don’t do anything useful.</p><h2 id="sample-programs" class="group relative"><a data-discover="true" href="#sample-programs" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Sample programs permalink"/></a>Sample programs</h2><p>I wrote (composed?) <code>echo.mid</code> from scratch in <a href="https://www.ableton.com" rel="nofollow">Ableton</a> and transpiled the rest from brainfuck.</p><div class="overflow-auto"><table><thead><tr><th>MIDI</th><th>Description</th><th>Audio (synthesized using <a href="https://en.wikipedia.org/wiki/TiMidity%2B%2B" rel="nofollow">TiMidity</a>)</th><th>Source</th></tr></thead><tbody><tr><td><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/hello_world.mid" rel="nofollow"><code>hello_world.mid</code></a></td><td>Prints <code>Hello, World!</code></td><td><audio controls="" preload="" src="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/hello_world.mp3"><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/hello_world.mp3" rel="nofollow" download="hello_world.mp3">Download hello_world.mp3</a></audio></td><td><a href="https://esolangs.org/wiki/Brainfuck#Hello,_World!" rel="nofollow">Esolang Wiki</a></td></tr><tr><td><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/cell_width.mid" rel="nofollow"><code>cell_width.mid</code></a></td><td>Prints the interpreter’s cell width</td><td><audio controls="" preload="" src="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/cell_width.mp3"><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/cell_width.mp3" rel="nofollow" download="cell_width.mp3">Download cell_width.mp3</a></audio></td><td><a href="https://esolangs.org/wiki/Brainfuck#Cell_size" rel="nofollow">Esolang Wiki</a></td></tr><tr><td><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/echo.mid" rel="nofollow"><code>echo.mid</code></a></td><td>Prints the input, like <a href="https://en.wikipedia.org/wiki/Echo_(command)" rel="nofollow"><code>echo</code></a></td><td><audio controls="" preload="" src="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/echo.mp3"><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/echo.mp3" rel="nofollow" download="echo.mp3">Download echo.mp3</a></audio></td><td>Me</td></tr><tr><td><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/wc.mid" rel="nofollow"><code>wc.mid</code></a></td><td>Counts input lines, words, and bytes, like <a href="https://en.wikipedia.org/wiki/Wc_(Unix)" rel="nofollow"><code>wc</code></a></td><td><audio controls="" preload="" src="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/wc.mp3"><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/wc.mp3" rel="nofollow" download="wc.mp3">Download wc.mp3</a></audio></td><td><a href="https://brainfuck.org" rel="nofollow">Daniel B. Cristofani</a></td></tr><tr><td><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/fib.mid" rel="nofollow"><code>fib.mid</code></a></td><td>Prints the entire <a href="https://en.wikipedia.org/wiki/Fibonacci_sequence" rel="nofollow">Fibonacci sequence</a></td><td><audio controls="" preload="" src="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/fib.mp3"><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/fib.mp3" rel="nofollow" download="fib.mp3">Download fib.mp3</a></audio></td><td><a href="https://brainfuck.org" rel="nofollow">Daniel B. Cristofani</a></td></tr><tr><td><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/bubble_sort.mid" rel="nofollow"><code>bubble_sort.mid</code></a></td><td>Sorts the input bytes using <a href="https://en.wikipedia.org/wiki/Bubble_sort" rel="nofollow">bubble sort</a></td><td><audio controls="" preload="" src="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/bubble_sort.mp3"><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/bubble_sort.mp3" rel="nofollow" download="bubble_sort.mp3">Download bubble_sort.mp3</a></audio></td><td><a href="https://brainfuck.org" rel="nofollow">Daniel B. Cristofani</a></td></tr><tr><td><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/insertion_sort.mid" rel="nofollow"><code>insertion_sort.mid</code></a></td><td>Sorts the input bytes using <a href="https://en.wikipedia.org/wiki/Insertion_sort" rel="nofollow">insertion sort</a></td><td><audio controls="" preload="" src="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/insertion_sort.mp3"><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/insertion_sort.mp3" rel="nofollow" download="insertion_sort.mp3">Download insertion_sort.mp3</a></audio></td><td><a href="https://brainfuck.org" rel="nofollow">Daniel B. Cristofani</a></td></tr><tr><td><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/quick_sort.mid" rel="nofollow"><code>quick_sort.mid</code></a></td><td>Sorts the input bytes using <a href="https://en.wikipedia.org/wiki/Quicksort" rel="nofollow">quick sort</a></td><td><audio controls="" preload="" src="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/quick_sort.mp3"><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/quick_sort.mp3" rel="nofollow" download="quick_sort.mp3">Download quick_sort.mp3</a></audio></td><td><a href="https://brainfuck.org" rel="nofollow">Daniel B. Cristofani</a></td></tr><tr><td><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/brainfuck_interpreter.mid" rel="nofollow"><code>brainfuck_interpreter.mid</code></a></td><td>Runs a brainfuck program on its input, which should be separated by an exclamation point</td><td><audio controls="" preload="" src="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/brainfuck_interpreter.mp3"><a href="https://github.com/TomerAberbach/schoenberg/raw/refs/heads/main/samples/brainfuck_interpreter.mp3" rel="nofollow" download="brainfuck_interpreter.mp3">Download brainfuck_interpreter.mp3</a></audio></td><td><a href="https://brainfuck.org" rel="nofollow">Daniel B. Cristofani</a></td></tr></tbody></table></div><p>All the programs can be found <a href="https://github.com/TomerAberbach/schoenberg/tree/main/samples" rel="nofollow">on GitHub</a>.</p><h2 id="faq" class="group relative"><a data-discover="true" href="#faq" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="FAQ permalink"/></a>FAQ</h2><h3 id="whats-with-the-name" class="group relative"><a data-discover="true" href="#whats-with-the-name" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="What’s with the name? permalink"/></a>What’s with the name?</h3><p>The programming language is named after <a href="https://en.wikipedia.org/wiki/Arnold_Schoenberg" rel="nofollow">Arnold Schoenberg</a> (1874–1951), who is widely considered to be the father of <a href="https://en.wikipedia.org/wiki/Atonality" rel="nofollow">atonal music</a>.</p><p>Writing <a href="https://en.wikipedia.org/wiki/Tonality#Tonal_musics" rel="nofollow"><em>tonal</em> music</a>, the basis of Western music composition, requires limiting yourself to the pitch classes of a chosen <a href="https://en.wikipedia.org/wiki/Key_(music)" rel="nofollow">key</a>, but this is incredibly hard to do when writing Schoenberg programs.</p><p>For example, if you want to decrement the pointer cell, then <a href="#syntax">the next note <em>must</em> be 1 pitch class away</a>. This leaves you with only two options for the next note’s pitch class, but it’s possible neither of those pitch classes is in the chosen key.</p><p>As a result, Schoenberg programs tend to be atonal.</p><h3 id="how-did-you-come-up-with-the-idea" class="group relative"><a data-discover="true" href="#how-did-you-come-up-with-the-idea" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="How did you come up with the idea? permalink"/></a>How did you come up with the idea?</h3><p>I was inspired by <a href="https://www.dangermouse.net/esoteric/piet.html" rel="nofollow">Piet</a>, an esoteric programming language where programs look like abstract paintings.</p><p>The concept of a program language where programs are written in an artistic medium was intriguing to me, so I decided to create my own programming language based on music composition, the artistic medium <a href="/?tags=tracks">I have the most experience with</a>.</p><h3 id="is-it-useful-for-anything" class="group relative"><a data-discover="true" href="#is-it-useful-for-anything" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Is it useful for anything? permalink"/></a>Is it useful for anything?</h3><p>Like most esoteric programming languages, Schoenberg is mostly a meme and mostly useless.</p><p>It could be used for <a href="https://en.wikipedia.org/wiki/Steganography" rel="nofollow">steganography</a> though. For example, you could <a href="https://copy.sh/brainfuck/text.html" rel="nofollow">create a brainfuck program that outputs some text</a> and further conceal the message by transpiling it to a Schoenberg program.</p><h3 id="is-there-a-one-to-one-mapping-between-brainfuck-and-schoenberg-programs" class="group relative"><a data-discover="true" href="#is-there-a-one-to-one-mapping-between-brainfuck-and-schoenberg-programs" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Is there a one-to-one mapping between brainfuck and Schoenberg programs? permalink"/></a>Is there a one-to-one mapping between brainfuck and Schoenberg programs?</h3><p>No, there is a one-to-many mapping for many reasons. Here are a few:</p><ul><li>Schoenberg programs can be <a href="https://en.wikipedia.org/wiki/Transposition_(music)" rel="nofollow">transposed</a> without affecting behavior.</li><li>Schoenberg’s <a href="#syntax">syntax</a> allows specifying most commands in more than one way.</li><li>Note length can often be changed without affecting behavior.</li></ul><h3 id="why-did-you-write-it-in-rust" class="group relative"><a data-discover="true" href="#why-did-you-write-it-in-rust" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Why did you write it in Rust? permalink"/></a>Why did you write it in Rust?</h3><p>Rust is pretty well-suited for writing interpreters, compilers, transpilers, etc. because of its <a href="https://en.wikipedia.org/wiki/Algebraic_data_type" rel="nofollow">“algebraic data type”</a> style <a href="https://doc.rust-lang.org/std/keyword.enum.html" rel="nofollow">enums</a>. It also has a user-friendly and performant MIDI parsing and writing library called <a href="https://docs.rs/midly/latest/midly" rel="nofollow"><code>midly</code></a>.</p><p>Plus, I’ve rarely used Rust and this seemed like a good opportunity to learn more about it.</p><h3 id="is-there-an-ide" class="group relative"><a data-discover="true" href="#is-there-an-ide" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Is there an IDE? permalink"/></a>Is there an IDE?</h3><p>Any MIDI editor is a Schoenberg IDE!</p><p>I personally use <a href="https://www.ableton.com" rel="nofollow">Ableton</a> because that’s what I use for non-Schoenberg music production, but <a href="https://www.apple.com/mac/garageband" rel="nofollow">GarageBand</a> and <a href="https://www.image-line.com" rel="nofollow">FL Studio</a> are also good options.</p>]]></content:encoded>
                <description><![CDATA[Schoenberg is an esoteric programming language where programs are written as MIDI files. A MIDI file is basically digital sheet music that tells a computer which notes to play when and how loudly. The…]]></description>
                <guid isPermaLink="true">https://tomeraberba.ch/schoenberg</guid>
                <pubDate>Mon, 23 Jun 2025 00:00:00 GMT</pubDate>
                
              </item>
            
              <item>
                <title><![CDATA[Engineering Mindset]]></title>
                <link>https://tomeraberba.ch/engineering-mindset</link>
                <category><![CDATA[code]]></category><category><![CDATA[engineering]]></category><category><![CDATA[learning]]></category>
                <content:encoded><![CDATA[<p>I recently left Google and during my last two weeks several people asked me the same question:</p><blockquote><p>How did you get to where you are?</p></blockquote><p>What they were asking is how I developed my software engineering skills to their current levels. Over those two weeks I refined my answer and wrote it up in case it comes up again or is helpful to others.</p><h2 id="surface-level-vs-deep-understanding" class="group relative"><a data-discover="true" href="#surface-level-vs-deep-understanding" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Surface level vs deep understanding permalink"/></a>Surface level vs deep understanding</h2><p>People frequently stick to surface level thinking. Maybe not always and maybe not entirely, but often people don’t strive, knowingly or unknowingly, to dig deeper.</p><h3 id="an-example" class="group relative"><a data-discover="true" href="#an-example" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="An example permalink"/></a>An example</h3><p>Let’s say someone is reviewing your code. They leave a comment asking to change A to B.</p><p>The worst thing you can do is just make the change without understanding the reasoning, because you learn nothing.</p><p>The slightly better, but still not great, thing you can do is understand why changing A to B is beneficial in that particular scenario. You probably learn something, but it’s still surface level understanding because it’s unlikely you’ll be able to apply the lesson to future scenarios.</p><p>The best thing you can do is understand the principle that prompted the reviewer to leave the comment. It’s unlikely the reviewer has ever seen the exact code you sent for review. So how did they know to leave the comment? They must have consulted a larger or deeper principle, knowingly or unknowingly, to come to their conclusion.</p><h3 id="mental-model-building" class="group relative"><a data-discover="true" href="#mental-model-building" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Mental model building permalink"/></a>Mental model building</h3><p>Those principles are exactly what you want to collect and understand. Using those principles you can build a strong <a href="https://en.wikipedia.org/wiki/Mental_model" rel="nofollow">mental model</a> of what makes good code, good technical design, good software engineering, etc. Then you can rigorously apply that mental model to future scenarios.</p><p>I used code reviews as an example, but this idea applies to any type of feedback or decision making you might encounter.</p><p>Naturally, as you do this you might collect principles that conflict with each other. That likely means that some principles you worked into your mental model in the past were too broad or imprecise. Collecting more principles allows you to revise your mental model and make it more robust and precise over time.</p><p>Learning is an iterative approach. Don’t be afraid to admit when you are wrong and revise your understanding!</p><h3 id="strategies" class="group relative"><a data-discover="true" href="#strategies" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Strategies permalink"/></a>Strategies</h3><p>You might be thinking that this sounds great in theory, but how does it work in practice? A good strategy for collecting principles is asking questions and following up with independent research as needed.</p><p>For example, going back to the code review, let’s say you don’t understand why the reviewer thinks B is better than A. Don’t sit there wondering. Ask them why! If their initial answer doesn’t reveal the principle, then continue asking “why” questions until the principle is clear or until you’ve learned enough to do your own research on the subject.</p><div class="relative my-10 rounded-md border-l-4 p-8 border-l-yellow-500 bg-yellow-100" role="note"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M12%209v3.75m9-.75a9%209%200%201%201-18%200%209%209%200%200%201%2018%200Zm-9%203.75h.008v.008H12v-.008Z&#x27;%20/%3e%3c/svg%3e" alt="" class="not-prose absolute top-0 left-0 m-0 box-content size-9 -translate-x-1/2 -translate-y-[45%] rounded-full border-4 border-yellow-100 bg-yellow-100"/><header class="pb-2 text-base font-semibold uppercase">Warning</header><div class="*:first:mt-0 *:last:mb-0"><p>It’s not uncommon to discover that there was no valid principle underpinning the feedback or decision, and that it was based entirely on “vibes”. Do yourself a favor and leave vibes out of your mental model.</p></div></div><h2 id="my-answer" class="group relative"><a data-discover="true" href="#my-answer" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="My answer permalink"/></a>My answer</h2><p>So my answer, after explaining all that, is that I’ve always dug for the deeper principles, from my early beginnings, and that’s enabled me to rapidly grow as a software engineer.</p>]]></content:encoded>
                <description><![CDATA[I recently left Google and during my last two weeks several people asked me the same question: How did you get to where you are? What they were asking is how I developed my software engineering skills…]]></description>
                <guid isPermaLink="true">https://tomeraberba.ch/engineering-mindset</guid>
                <pubDate>Fri, 20 Sep 2024 00:00:00 GMT</pubDate>
                
              </item>
            
              <item>
                <title><![CDATA[The 1 Hour per Year Bug (But Only in Pacific Time!)]]></title>
                <link>https://tomeraberba.ch/the-1-hour-per-year-bug</link>
                <category><![CDATA[bugs]]></category><category><![CDATA[code]]></category><category><![CDATA[dates]]></category><category><![CDATA[docs]]></category><category><![CDATA[engineering]]></category><category><![CDATA[google]]></category>
                <content:encoded><![CDATA[<div class="relative my-10 rounded-md border-l-4 p-8 border-l-blue-500 bg-blue-50" role="note"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;%23374151&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M11.25%2011.25l.041-.02a.75.75%200%20011.063.852l-.708%202.836a.75.75%200%20001.063.853l.041-.021M21%2012a9%209%200%2011-18%200%209%209%200%200118%200zm-9-3.75h.008v.008H12V8.25z&#x27;%20/%3e%3c/svg%3e" alt="" class="not-prose absolute top-0 left-0 m-0 box-content size-9 -translate-x-1/2 -translate-y-[45%] rounded-full border-4 border-blue-50 bg-blue-50"/><header class="pb-2 text-base font-semibold uppercase">Note</header><div class="*:first:mt-0 *:last:mb-0"><p>This article was updated in response to some helpful comments on <a href="https://news.ycombinator.com/item?id=41267894" rel="nofollow">Hacker News</a>. I originally incorrectly formatted the Pacific Time Zone daylight saving times relative to the Eastern Time Zone!</p></div></div><p>The date was November 8, 2021 and I was a <a href="https://en.wikipedia.org/wiki/Bug_triage" rel="nofollow">bug triager</a> on the Google Docs team. That day began like any other. I made myself some coffee and started looking through bug reports from the day before. But then something caught my eye.</p><h2 id="the-bug-reports-" class="group relative"><a data-discover="true" href="#the-bug-reports-" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="The bug reports &lt;span aria-label=&quot;bug&quot; role=&quot;img&quot;&gt;🐛&lt;/span&gt; permalink"/></a>The bug reports <span aria-label="bug" role="img">🐛</span></h2><p>There were an unusually large number of bugs reported and they all said the same thing. The user had created a reply or new comment in a Doc, but its timestamp in the UI said it had been created “tomorrow”.</p><h2 id="the-pattern-" class="group relative"><a data-discover="true" href="#the-pattern-" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="The pattern &lt;span aria-label=&quot;globe with meridians&quot; role=&quot;img&quot;&gt;🌐&lt;/span&gt; permalink"/></a>The pattern <span aria-label="globe with meridians" role="img">🌐</span></h2><p>A pattern quickly emerged. These bugs were all reported:</p><ul><li>By users in the <a href="https://en.wikipedia.org/wiki/Pacific_Time_Zone" rel="nofollow">Pacific Time Zone (PT)</a></li><li>Between 11<div></div>and 11<div></div>(PT) on November 7</li></ul><p>Normally I would have dismissed that as a coincidence, but <a href="https://en.wikipedia.org/wiki/Daylight_saving_time" rel="nofollow">daylight saving time</a> also ended at 2</p><div></div>(PT) on November 7!<p></p><h2 id="the-investigation-️" class="group relative"><a data-discover="true" href="#the-investigation-️" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="The investigation &lt;span aria-label=&quot;detective&quot; role=&quot;img&quot;&gt;🕵️&lt;/span&gt; permalink"/></a>The investigation <span aria-label="detective" role="img">🕵️</span></h2><p>This bug piqued my interest so I decided to triage it to myself.</p><p>It didn’t take long to conclude that the bug must be in <a href="https://github.com/google/closure-library/blob/334543f9e480564fcc8b9a38dee0fe13a3f42fc0/closure/goog/date/relative.js#L386-L419" rel="nofollow">Closure Library’s relative date formatting logic</a>, which Docs was using. The code started with trying to compute the number of days between the current time and the input time by:</p><ol><li>Getting the current time as a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date" rel="nofollow"><code>Date</code></a> object (i.e. <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date#parameters" rel="nofollow"><code>new Date()</code></a>).</li><li>Resetting the object’s hours, minutes, seconds, and milliseconds to zero so that it represented the current day’s start time.</li><li>Computing the number of milliseconds between the current day’s start time and the input time, dividing it by the number of milliseconds in a day, and rounding down.</li></ol><p>I stared at the code for a while and then it hit me! The input time, between 11</p><div></div>and 11<div></div>(PT) on November 7, was in the Pacific <em>Standard</em> Time Zone (PST), <em>after</em> daylight saving time ended, but the start of the current day (12<div></div>) was in the Pacific <em>Daylight</em> Time Zone (PDT), <em>before</em> daylight saving time ended.<p></p><p>The result? There weren’t 23 hours between 12</p><div></div>and 11<div></div>that day. The two times were in two different time zones, 1 hour apart, so there were 24 hours between the two times; a whole day!<p></p><svg aria-roledescription="flowchart-v2" class="flowchart" id="mermaid-0" style="font-family:Kantumruy Pro;font-size:16px;fill:#333;max-width:1091.859375px" viewBox="0 -50 1091.859 120" width="100%" xmlns="http://www.w3.org/2000/svg"><style>@keyframes edge-animation-frame{0%{stroke-dashoffset:0}}@keyframes dash{to{stroke-dashoffset:0}}#mermaid-0 .edge-thickness-normal{stroke-width:1px}#mermaid-0 .edge-pattern-solid{stroke-dasharray:0}#mermaid-0 .marker{fill:#0b0b0b;stroke:#0b0b0b}#mermaid-0 .marker.cross{stroke:#0b0b0b}#mermaid-0 svg{font-family:Kantumruy Pro;font-size:16px}#mermaid-0 p{margin:0}#mermaid-0 .label{font-family:Kantumruy Pro;color:#333}#mermaid-0 .label text,#mermaid-0 span{fill:#333;color:#333}#mermaid-0 .node circle,#mermaid-0 .node path,#mermaid-0 .node rect{fill:#fff4dd;stroke:#edb;stroke-width:1px}#mermaid-0 .node .label text{text-anchor:middle}#mermaid-0 .node .label{text-align:center}#mermaid-0 .flowchart-link{stroke:#0b0b0b;fill:none}#mermaid-0 .edgeLabel{background-color:#fdd;text-align:center}#mermaid-0 .edgeLabel p{background-color:#fdd}#mermaid-0 .edgeLabel rect{opacity:.5;background-color:#fdd;fill:#fdd}#mermaid-0 .labelBkg{background-color:rgba(244,221,255,.5)}#mermaid-0 :root{--mermaid-font-family:Kantumruy Pro}</style><marker class="flowchart-v2 marker" id="mermaid-0_flowchart-v2-pointEnd" markerHeight="8" markerUnits="userSpaceOnUse" markerWidth="8" orient="auto" refX="5" refY="5" viewBox="0 0 10 10"><path class="arrowMarkerPath" d="m0 0 10 5-10 5z" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker" id="mermaid-0_flowchart-v2-pointStart" markerHeight="8" markerUnits="userSpaceOnUse" markerWidth="8" orient="auto" refX="4.5" refY="5" viewBox="0 0 10 10"><path class="arrowMarkerPath" d="m0 5 10 5V0z" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker" id="mermaid-0_flowchart-v2-circleEnd" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="11" refY="5" viewBox="0 0 10 10"><circle class="arrowMarkerPath" cx="5" cy="5" r="5" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker class="flowchart-v2 marker" id="mermaid-0_flowchart-v2-circleStart" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="-1" refY="5" viewBox="0 0 10 10"><circle class="arrowMarkerPath" cx="5" cy="5" r="5" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker class="flowchart-v2 marker cross" id="mermaid-0_flowchart-v2-crossEnd" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="12" refY="5.2" viewBox="0 0 11 11"><path class="arrowMarkerPath" d="m1 1 9 9m0-9-9 9" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker cross" id="mermaid-0_flowchart-v2-crossStart" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="-1" refY="5.2" viewBox="0 0 11 11"><path class="arrowMarkerPath" d="m1 1 9 9m0-9-9 9" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><g class="root"><g class="edgePaths"><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M129.859 35h102.344" data-edge="true" data-et="edge" data-id="L_START_TIME_DST_ENDED_TIME_PDT_0" data-points="W3sieCI6MTI5Ljg1OTM3NSwieSI6MzV9LHsieCI6MTgzLjAzMTI1LCJ5IjozNX0seyJ4IjoyMzYuMjAzMTI1LCJ5IjozNX1d" id="L_START_TIME_DST_ENDED_TIME_PDT_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M397.688 35h246" data-edge="true" data-et="edge" data-id="L_DST_ENDED_TIME_PDT_DST_ENDED_TIME_PST_0" data-points="W3sieCI6Mzk3LjY4NzUsInkiOjM1fSx7IngiOjUyMi42ODc1LCJ5IjozNX0seyJ4Ijo2NDcuNjg3NSwieSI6MzV9XQ==" id="L_DST_ENDED_TIME_PDT_DST_ENDED_TIME_PST_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M804.594 35h111.281" data-edge="true" data-et="edge" data-id="L_DST_ENDED_TIME_PST_INPUT_TIME_0" data-points="W3sieCI6ODA0LjU5Mzc1LCJ5IjozNX0seyJ4Ijo4NjIuMjM0Mzc1LCJ5IjozNX0seyJ4Ijo5MTkuODc1LCJ5IjozNX1d" id="L_DST_ENDED_TIME_PST_INPUT_TIME_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path></g><g class="edgeLabels"><g class="edgeLabel"><foreignObject height="24" width="56.344" class="label" data-id="L_START_TIME_DST_ENDED_TIME_PDT_0" transform="translate(154.86 23)"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"><p>2 hours</p></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="48" width="200" class="label" data-id="L_DST_ENDED_TIME_PDT_DST_ENDED_TIME_PST_0" transform="translate(422.688 11)"><div style="display:table;white-space:break-spaces;line-height:1.5;max-width:200px;text-align:center;width:200px" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"><p>0 hours (clock turns back an hour)</p></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="24" width="65.281" class="label" data-id="L_DST_ENDED_TIME_PST_INPUT_TIME_0" transform="translate(829.594 23)"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"><p>22 hours</p></span></div></foreignObject></g></g><g class="nodes"><g class="default node" id="flowchart-START_TIME-0"><path class="basic label-container" d="M8 8h121.859v54H7.999z"></path><g class="label" transform="translate(38 23)"><rect></rect><foreignObject height="24" width="61.859"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>12:00am</p></span></div></foreignObject></g></g><g class="default node" id="flowchart-DST_ENDED_TIME_PDT-1"><path class="basic label-container" d="M236.203 8h161.484v54H236.203z"></path><g class="label" transform="translate(266.203 23)"><rect></rect><foreignObject height="24" width="101.484"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>2:00am (PDT)</p></span></div></foreignObject></g></g><g class="default node" id="flowchart-DST_ENDED_TIME_PST-2"><path class="basic label-container" d="M647.688 8h156.906v54H647.688z"></path><g class="label" transform="translate(677.688 23)"><rect></rect><foreignObject height="24" width="96.906"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>1:00am (PST)</p></span></div></foreignObject></g></g><g class="default node" id="flowchart-INPUT_TIME-3"><path class="basic label-container" d="M919.875 8h163.984v54H919.875z"></path><g class="label" transform="translate(949.875 23)"><rect></rect><foreignObject height="24" width="103.984"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>11:00pm (PST)</p></span></div></foreignObject></g></g></g></g><text style="text-anchor:middle;font-size:18px;fill:#333" x="545.93" y="-25">24 hours</text></svg><p>And what does the code do when the input time is 1 day later than the start of the current day? It formats the time as “tomorrow”… <span aria-label="clown face" role="img">🤡</span></p><h2 id="the-fix-" class="group relative"><a data-discover="true" href="#the-fix-" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="The fix &lt;span aria-label=&quot;wrench&quot; role=&quot;img&quot;&gt;🔧&lt;/span&gt; permalink"/></a>The fix <span aria-label="wrench" role="img">🔧</span></h2><p>So how did I fix this?</p><p>Luckily, JavaScript’s <code>Date</code> class exposes a handy <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/getTimezoneOffset" rel="nofollow"><code>getTimezoneOffset()</code></a> method, which returns the number of minutes between the <code>Date</code> object’s time zone and the <a href="https://en.wikipedia.org/wiki/UTC" rel="nofollow">UTC time zone</a>.</p><p><a href="https://github.com/google/closure-library/commit/84c93721c3ced2271541ae86fec9f85e9c24d991" rel="nofollow">I used that method</a> to compute the difference in milliseconds between the time zone offsets of the current day’s start time and the current time, and subtracted that from the number of milliseconds between the current day’s start time and the input time (in step 3 before).</p><p>What this effectively does is remove any millisecond differences that are only due to the time zone changing between the current time and the current day’s start time. So now the number of hours between 12</p><div></div>and 11<div></div>on that day is computed as 23!<p></p><h2 id="bonus-bug-" class="group relative"><a data-discover="true" href="#bonus-bug-" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Bonus bug! &lt;span aria-label=&quot;beetle&quot; role=&quot;img&quot;&gt;🪲&lt;/span&gt; permalink"/></a>Bonus bug! <span aria-label="beetle" role="img">🪲</span></h2><p>This bug actually also extended to when daylight saving time <em>starts</em>.</p><p>In 2021, daylight saving time started at 2</p><div></div>(PT) on March 14. So what would happen for an input time of 12<div></div>(PT) on March 15 when the current time was 11<div></div>(PT) on March 14?<p></p><p>You would expect there to be 23 hours between 12</p><div></div>, the start of the day, and 11<div></div>(PT) on March 14, but there are only 22 hours. Due to daylight saving time starting at 2<div></div>, which is actually the same time as 3<div></div>, there are only 23 hours between the start times of March 14 and 15!<p></p><svg aria-roledescription="flowchart-v2" class="flowchart" id="mermaid-1" style="font-family:Kantumruy Pro;font-size:16px;fill:#333;max-width:1095.125px" viewBox="0 -50 1095.125 120" width="100%" xmlns="http://www.w3.org/2000/svg"><style>@keyframes edge-animation-frame{0%{stroke-dashoffset:0}}@keyframes dash{to{stroke-dashoffset:0}}#mermaid-1 .edge-thickness-normal{stroke-width:1px}#mermaid-1 .edge-pattern-solid{stroke-dasharray:0}#mermaid-1 .marker{fill:#0b0b0b;stroke:#0b0b0b}#mermaid-1 .marker.cross{stroke:#0b0b0b}#mermaid-1 svg{font-family:Kantumruy Pro;font-size:16px}#mermaid-1 p{margin:0}#mermaid-1 .label{font-family:Kantumruy Pro;color:#333}#mermaid-1 .label text,#mermaid-1 span{fill:#333;color:#333}#mermaid-1 .node circle,#mermaid-1 .node path,#mermaid-1 .node rect{fill:#fff4dd;stroke:#edb;stroke-width:1px}#mermaid-1 .node .label text{text-anchor:middle}#mermaid-1 .node .label{text-align:center}#mermaid-1 .flowchart-link{stroke:#0b0b0b;fill:none}#mermaid-1 .edgeLabel{background-color:#fdd;text-align:center}#mermaid-1 .edgeLabel p{background-color:#fdd}#mermaid-1 .edgeLabel rect{opacity:.5;background-color:#fdd;fill:#fdd}#mermaid-1 .labelBkg{background-color:rgba(244,221,255,.5)}#mermaid-1 :root{--mermaid-font-family:Kantumruy Pro}</style><marker class="flowchart-v2 marker" id="mermaid-1_flowchart-v2-pointEnd" markerHeight="8" markerUnits="userSpaceOnUse" markerWidth="8" orient="auto" refX="5" refY="5" viewBox="0 0 10 10"><path class="arrowMarkerPath" d="m0 0 10 5-10 5z" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker" id="mermaid-1_flowchart-v2-pointStart" markerHeight="8" markerUnits="userSpaceOnUse" markerWidth="8" orient="auto" refX="4.5" refY="5" viewBox="0 0 10 10"><path class="arrowMarkerPath" d="m0 5 10 5V0z" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker" id="mermaid-1_flowchart-v2-circleEnd" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="11" refY="5" viewBox="0 0 10 10"><circle class="arrowMarkerPath" cx="5" cy="5" r="5" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker class="flowchart-v2 marker" id="mermaid-1_flowchart-v2-circleStart" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="-1" refY="5" viewBox="0 0 10 10"><circle class="arrowMarkerPath" cx="5" cy="5" r="5" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker class="flowchart-v2 marker cross" id="mermaid-1_flowchart-v2-crossEnd" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="12" refY="5.2" viewBox="0 0 11 11"><path class="arrowMarkerPath" d="m1 1 9 9m0-9-9 9" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker cross" id="mermaid-1_flowchart-v2-crossStart" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="-1" refY="5.2" viewBox="0 0 11 11"><path class="arrowMarkerPath" d="m1 1 9 9m0-9-9 9" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><g class="root"><g class="edgePaths"><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M129.859 35h102.344" data-edge="true" data-et="edge" data-id="L_START_TIME_DST_STARTED_TIME_PST_0" data-points="W3sieCI6MTI5Ljg1OTM3NSwieSI6MzV9LHsieCI6MTgzLjAzMTI1LCJ5IjozNX0seyJ4IjoyMzYuMjAzMTI1LCJ5IjozNX1d" id="L_START_TIME_DST_STARTED_TIME_PST_0" marker-end="url(#mermaid-1_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M396.25 35h246" data-edge="true" data-et="edge" data-id="L_DST_STARTED_TIME_PST_DST_STARTED_TIME_PDT_0" data-points="W3sieCI6Mzk2LjI1LCJ5IjozNX0seyJ4Ijo1MjEuMjUsInkiOjM1fSx7IngiOjY0Ni4yNSwieSI6MzV9XQ==" id="L_DST_STARTED_TIME_PST_DST_STARTED_TIME_PDT_0" marker-end="url(#mermaid-1_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M807.688 35h108.156" data-edge="true" data-et="edge" data-id="L_DST_STARTED_TIME_PDT_INPUT_TIME_0" data-points="W3sieCI6ODA3LjY4NzUsInkiOjM1fSx7IngiOjg2My43NjU2MjUsInkiOjM1fSx7IngiOjkxOS44NDM3NSwieSI6MzV9XQ==" id="L_DST_STARTED_TIME_PDT_INPUT_TIME_0" marker-end="url(#mermaid-1_flowchart-v2-pointEnd)"></path></g><g class="edgeLabels"><g class="edgeLabel"><foreignObject height="24" width="56.344" class="label" data-id="L_START_TIME_DST_STARTED_TIME_PST_0" transform="translate(154.86 23)"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"><p>2 hours</p></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="48" width="200" class="label" data-id="L_DST_STARTED_TIME_PST_DST_STARTED_TIME_PDT_0" transform="translate(421.25 11)"><div style="display:table;white-space:break-spaces;line-height:1.5;max-width:200px;text-align:center;width:200px" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"><p>0 hours (clock turns forward an hour)</p></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="24" width="62.156" class="label" data-id="L_DST_STARTED_TIME_PDT_INPUT_TIME_0" transform="translate(832.688 23)"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"><p>21 hours</p></span></div></foreignObject></g></g><g class="nodes"><g class="default node" id="flowchart-START_TIME-0"><path class="basic label-container" d="M8 8h121.859v54H7.999z"></path><g class="label" transform="translate(38 23)"><rect></rect><foreignObject height="24" width="61.859"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>12:00am</p></span></div></foreignObject></g></g><g class="default node" id="flowchart-DST_STARTED_TIME_PST-1"><path class="basic label-container" d="M236.204 8H396.25v54H236.204z"></path><g class="label" transform="translate(266.203 23)"><rect></rect><foreignObject height="24" width="100.047"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>2:00am (PST)</p></span></div></foreignObject></g></g><g class="default node" id="flowchart-DST_STARTED_TIME_PDT-2"><path class="basic label-container" d="M646.25 8h161.438v54H646.25z"></path><g class="label" transform="translate(676.25 23)"><rect></rect><foreignObject height="24" width="101.438"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>3:00am (PDT)</p></span></div></foreignObject></g></g><g class="default node" id="flowchart-INPUT_TIME-3"><path class="basic label-container" d="M919.843 8h167.281v54h-167.28z"></path><g class="label" transform="translate(949.844 23)"><rect></rect><foreignObject height="24" width="107.281"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>12:00am (PDT)</p></span></div></foreignObject></g></g></g></g><text style="text-anchor:middle;font-size:18px;fill:#333" x="547.563" y="-25">23 hours</text></svg><p>And what does the code do for that number of hours? It computes the number of days between the two times as zero due to rounding down, and formats the time as “today”…</p><div class="relative my-10 rounded-md border-l-4 p-8 border-l-blue-500 bg-blue-50" role="note"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;%23374151&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M11.25%2011.25l.041-.02a.75.75%200%20011.063.852l-.708%202.836a.75.75%200%20001.063.853l.041-.021M21%2012a9%209%200%2011-18%200%209%209%200%200118%200zm-9-3.75h.008v.008H12V8.25z&#x27;%20/%3e%3c/svg%3e" alt="" class="not-prose absolute top-0 left-0 m-0 box-content size-9 -translate-x-1/2 -translate-y-[45%] rounded-full border-4 border-blue-50 bg-blue-50"/><header class="pb-2 text-base font-semibold uppercase">Note</header><div class="*:first:mt-0 *:last:mb-0"><p>This bug never actually happened in Docs because you can’t create a comment or reply in the future and view it from the past.</p></div></div><h2 id="why-1-hour-per-year-but-only-in-pacific-time-" class="group relative"><a data-discover="true" href="#why-1-hour-per-year-but-only-in-pacific-time-" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Why 1 hour per year, but only in Pacific Time? &lt;span aria-label=&quot;thinking face&quot; role=&quot;img&quot;&gt;🤔&lt;/span&gt; permalink"/></a>Why 1 hour per year, but only in Pacific Time? <span aria-label="thinking face" role="img">🤔</span></h2><p>I originally wrote an explanation here, but some comments on <a href="https://news.ycombinator.com/item?id=41267894" rel="nofollow">Hacker News</a> made me realize that this bug could have theoretically happened in any time zone.</p><p>It’s possible that all the bug reports were coincidentally from users in the Pacific Time Zone. Or perhaps there’s some other factor at play that I haven’t thought of. Let me know if you have any ideas!</p><h2 id="conclusion-️" class="group relative"><a data-discover="true" href="#conclusion-️" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Conclusion &lt;span aria-label=&quot;judge&quot; role=&quot;img&quot;&gt;🧑‍⚖️&lt;/span&gt; permalink"/></a>Conclusion <span aria-label="judge" role="img">🧑‍⚖️</span></h2><p>At the time of writing, August 2024, my bug fix has enjoyed two glorious hours of usefulness. Time well spent I guess? <span aria-label="grinning face with sweat" role="img">😅</span></p><p>Here’s to hoping my bug fix becomes <a href="https://en.wikipedia.org/wiki/Sunshine_Protection_Act" rel="nofollow">obsolete</a> soon!</p>]]></content:encoded>
                <description><![CDATA[:::note This article was updated in response to some helpful comments on Hacker News. I originally incorrectly formatted the Pacific Time Zone daylight saving times relative to the Eastern Time Zone!…]]></description>
                <guid isPermaLink="true">https://tomeraberba.ch/the-1-hour-per-year-bug</guid>
                <pubDate>Thu, 15 Aug 2024 00:00:00 GMT</pubDate>
                <lastBuildDate>Wed, 21 Aug 2024 00:00:00 GMT</lastBuildDate>
              </item>
            
              <item>
                <title><![CDATA[lonely cosmopolite]]></title>
                <link>https://tomeraberba.ch/lonely-cosmopolite</link>
                <category><![CDATA[jazz]]></category><category><![CDATA[lofi]]></category><category><![CDATA[music]]></category><category><![CDATA[piano]]></category><category><![CDATA[tracks]]></category>
                <content:encoded><![CDATA[<p>I composed, produced, and released an instrumental lofi track!</p><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowFullScreen="" frameBorder="0" height="150" src="https://www.youtube.com/embed/g54wSssQCTA?feature=oembed" title="lonely cosmopolite" width="200"></iframe><p>You can also find the track on <a href="https://open.spotify.com/track/62D6HKfuNWbQQQHXiIhXOn" rel="nofollow">Spotify</a>, <a href="https://music.apple.com/us/album/lonely-cosmopolite-single/1759658603" rel="nofollow">Apple Music</a>, <a href="https://music.youtube.com/watch?v=g54wSssQCTA" rel="nofollow">YouTube Music</a>, and other music streaming services. Special thanks to <a href="https://www.instagram.com/hello._neko" rel="nofollow">Amanda Gomes</a> for the album cover!</p>]]></content:encoded>
                <description><![CDATA[I composed, produced, and released an instrumental lofi track! https://www.youtube.com/watch?v=g54wSssQCTA You can also find the track on Spotify, Apple Music, YouTube Music, and other music streaming…]]></description>
                <guid isPermaLink="true">https://tomeraberba.ch/lonely-cosmopolite</guid>
                <pubDate>Mon, 05 Aug 2024 00:00:00 GMT</pubDate>
                
              </item>
            
              <item>
                <title><![CDATA[To Dedupe Then Sort or Sort Then Dedupe?]]></title>
                <link>https://tomeraberba.ch/to-dedupe-then-sort-or-sort-then-dedupe</link>
                <category><![CDATA[code]]></category><category><![CDATA[data structures]]></category><category><![CDATA[javascript]]></category><category><![CDATA[performance]]></category>
                <content:encoded><![CDATA[<link rel="preload" as="image" href="/assets/efficiency-of-sorting-and-deduping-CRzCd3Bv.svg"/><p>I recently came across a deceptively simple problem. I wanted to dedupe and sort a list of integers <a href="https://en.wikipedia.org/wiki/In-place_algorithm" rel="nofollow">in-place</a>.</p><p>My initial instinct was to first remove duplicates and then sort using the programming language’s built in sort function. In JavaScript that would look something like this:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> removeDuplicatesThenSort </span><span style="color:#89ddff">=</span><span style="color:#babed8;font-style:italic"> array</span><span style="color:#c792ea"> =&gt;</span><span style="color:#89ddff"> {</span></span>
<span class="line"><span style="color:#c792ea">  const</span><span style="color:#babed8"> seen</span><span style="color:#89ddff"> =</span><span style="color:#89ddff"> new</span><span style="color:#82aaff"> Set</span><span style="color:#f07178">()</span></span>
<span class="line"><span style="color:#c792ea">  let</span><span style="color:#babed8"> insertIndex</span><span style="color:#89ddff"> =</span><span style="color:#f78c6c"> 0</span></span>
<span class="line"><span style="color:#89ddff;font-style:italic">  for</span><span style="color:#f07178"> (</span><span style="color:#c792ea">let</span><span style="color:#babed8"> i</span><span style="color:#89ddff"> =</span><span style="color:#f78c6c"> 0</span><span style="color:#89ddff">;</span><span style="color:#babed8"> i</span><span style="color:#89ddff"> &lt;</span><span style="color:#babed8"> array</span><span style="color:#89ddff">.</span><span style="color:#babed8">length</span><span style="color:#89ddff">;</span><span style="color:#babed8"> i</span><span style="color:#89ddff">++</span><span style="color:#f07178">) </span><span style="color:#89ddff">{</span></span>
<span class="line"><span style="color:#c792ea">    const</span><span style="color:#babed8"> value</span><span style="color:#89ddff"> =</span><span style="color:#babed8"> array</span><span style="color:#f07178">[</span><span style="color:#babed8">i</span><span style="color:#f07178">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676e95;font-style:italic">    // Skip integers we&#x27;ve already seen.</span></span>
<span class="line"><span style="color:#89ddff;font-style:italic">    if</span><span style="color:#f07178"> (</span><span style="color:#babed8">seen</span><span style="color:#89ddff">.</span><span style="color:#82aaff">has</span><span style="color:#f07178">(</span><span style="color:#babed8">value</span><span style="color:#f07178">)) </span><span style="color:#89ddff">{</span></span>
<span class="line"><span style="color:#89ddff;font-style:italic">      continue</span></span>
<span class="line"><span style="color:#89ddff">    }</span></span>
<span class="line"><span style="color:#babed8">    seen</span><span style="color:#89ddff">.</span><span style="color:#82aaff">add</span><span style="color:#f07178">(</span><span style="color:#babed8">value</span><span style="color:#f07178">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676e95;font-style:italic">    // Place this unique integer at the next available index.</span></span>
<span class="line"><span style="color:#babed8">    array</span><span style="color:#f07178">[</span><span style="color:#babed8">insertIndex</span><span style="color:#f07178">] </span><span style="color:#89ddff">=</span><span style="color:#babed8"> value</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">    // Increment to place the next unique integer at the next index.</span></span>
<span class="line"><span style="color:#babed8">    insertIndex</span><span style="color:#89ddff">++</span></span>
<span class="line"><span style="color:#89ddff">  }</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">  // Shrink the array down to its new length.</span></span>
<span class="line"><span style="color:#babed8">  array</span><span style="color:#89ddff">.</span><span style="color:#babed8">length</span><span style="color:#89ddff"> =</span><span style="color:#babed8"> insertIndex</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">  array</span><span style="color:#89ddff">.</span><span style="color:#82aaff">sort</span><span style="color:#f07178">(</span><span style="color:#89ddff">(</span><span style="color:#babed8;font-style:italic">a</span><span style="color:#89ddff">,</span><span style="color:#babed8;font-style:italic"> b</span><span style="color:#89ddff">)</span><span style="color:#c792ea"> =&gt;</span><span style="color:#babed8"> a</span><span style="color:#89ddff"> -</span><span style="color:#babed8"> b</span><span style="color:#f07178">)</span></span>
<span class="line"><span style="color:#89ddff">}</span></span></code></pre><p>But then I realized this is not the only option. I could sort first and <em>then</em> remove duplicates:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> sortThenRemoveDuplicates </span><span style="color:#89ddff">=</span><span style="color:#babed8;font-style:italic"> array</span><span style="color:#c792ea"> =&gt;</span><span style="color:#89ddff"> {</span></span>
<span class="line"><span style="color:#babed8">  array</span><span style="color:#89ddff">.</span><span style="color:#82aaff">sort</span><span style="color:#f07178">(</span><span style="color:#89ddff">(</span><span style="color:#babed8;font-style:italic">a</span><span style="color:#89ddff">,</span><span style="color:#babed8;font-style:italic"> b</span><span style="color:#89ddff">)</span><span style="color:#c792ea"> =&gt;</span><span style="color:#babed8"> a</span><span style="color:#89ddff"> -</span><span style="color:#babed8"> b</span><span style="color:#f07178">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89ddff;font-style:italic">  if</span><span style="color:#f07178"> (</span><span style="color:#babed8">array</span><span style="color:#89ddff">.</span><span style="color:#babed8">length</span><span style="color:#89ddff"> ===</span><span style="color:#f78c6c"> 0</span><span style="color:#f07178">) </span><span style="color:#89ddff">{</span></span>
<span class="line"><span style="color:#89ddff;font-style:italic">    return</span></span>
<span class="line"><span style="color:#89ddff">  }</span></span>
<span class="line"><span style="color:#c792ea">  let</span><span style="color:#babed8"> insertIndex</span><span style="color:#89ddff"> =</span><span style="color:#f78c6c"> 1</span></span>
<span class="line"><span style="color:#89ddff;font-style:italic">  for</span><span style="color:#f07178"> (</span><span style="color:#c792ea">let</span><span style="color:#babed8"> i</span><span style="color:#89ddff"> =</span><span style="color:#f78c6c"> 1</span><span style="color:#89ddff">;</span><span style="color:#babed8"> i</span><span style="color:#89ddff"> &lt;</span><span style="color:#babed8"> array</span><span style="color:#89ddff">.</span><span style="color:#babed8">length</span><span style="color:#89ddff">;</span><span style="color:#babed8"> i</span><span style="color:#89ddff">++</span><span style="color:#f07178">) </span><span style="color:#89ddff">{</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">    // Skip sequences of duplicate integers.</span></span>
<span class="line"><span style="color:#c792ea">    const</span><span style="color:#babed8"> previous</span><span style="color:#89ddff"> =</span><span style="color:#babed8"> array</span><span style="color:#f07178">[</span><span style="color:#babed8">i</span><span style="color:#89ddff"> -</span><span style="color:#f78c6c"> 1</span><span style="color:#f07178">]</span></span>
<span class="line"><span style="color:#c792ea">    const</span><span style="color:#babed8"> current</span><span style="color:#89ddff"> =</span><span style="color:#babed8"> array</span><span style="color:#f07178">[</span><span style="color:#babed8">i</span><span style="color:#f07178">]</span></span>
<span class="line"><span style="color:#89ddff;font-style:italic">    if</span><span style="color:#f07178"> (</span><span style="color:#babed8">current</span><span style="color:#89ddff"> ===</span><span style="color:#babed8"> previous</span><span style="color:#f07178">) </span><span style="color:#89ddff">{</span></span>
<span class="line"><span style="color:#89ddff;font-style:italic">      continue</span></span>
<span class="line"><span style="color:#89ddff">    }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676e95;font-style:italic">    // Place this unique integer at the next available index.</span></span>
<span class="line"><span style="color:#babed8">    array</span><span style="color:#f07178">[</span><span style="color:#babed8">insertIndex</span><span style="color:#f07178">] </span><span style="color:#89ddff">=</span><span style="color:#babed8"> current</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">    // Increment to place the next unique integer at the next index.</span></span>
<span class="line"><span style="color:#babed8">    insertIndex</span><span style="color:#89ddff">++</span></span>
<span class="line"><span style="color:#89ddff">  }</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">  // Shrink the array down to its new length.</span></span>
<span class="line"><span style="color:#babed8">  array</span><span style="color:#89ddff">.</span><span style="color:#babed8">length</span><span style="color:#89ddff"> =</span><span style="color:#babed8"> insertIndex</span></span>
<span class="line"><span style="color:#89ddff">}</span></span></code></pre><p>At first I thought this option must be more efficient than the other one because it doesn’t require tracking already seen integers in a set. But then I had another realization: removing duplicates first could shrink the size of the array, and that could speed up the subsequent sorting.</p><p>So which option is better?</p><div class="relative my-10 rounded-md border-l-4 p-8 border-l-blue-500 bg-blue-50" role="note"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;%23374151&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M11.25%2011.25l.041-.02a.75.75%200%20011.063.852l-.708%202.836a.75.75%200%20001.063.853l.041-.021M21%2012a9%209%200%2011-18%200%209%209%200%200118%200zm-9-3.75h.008v.008H12V8.25z&#x27;%20/%3e%3c/svg%3e" alt="" class="not-prose absolute top-0 left-0 m-0 box-content size-9 -translate-x-1/2 -translate-y-[45%] rounded-full border-4 border-blue-50 bg-blue-50"/><header class="pb-2 text-base font-semibold uppercase">Note</header><div class="*:first:mt-0 *:last:mb-0"><p>We won’t explore the possibility of removing duplicates while sorting because that would require rewriting the programming language’s built in sorting algorithm and it’s unlikely that we can match the original performance<sup><a href="#fn-1" aria-describedby="footnote-label" data-footnote-ref="" id="fnref-1">1</a></sup>.</p></div></div><h2 id="time-and-space-complexity" class="group relative"><a data-discover="true" href="#time-and-space-complexity" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Time and space complexity permalink"/></a>Time and space complexity</h2><p><a href="https://en.wikipedia.org/wiki/Computational_complexity" rel="nofollow">Complexity analysis</a> is a way to reason about the amount of resources, usually time and space, an algorithm uses as the size of its input increases. We can use it to objectively compare our two algorithms!</p><p>Let’s define some variables that describe our input array:</p><ul><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.4306em"></span><span class="mord mathnormal">n</span></span></span></span> is the total number of integers</li><li><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><mo>≤</mo><mi>n</mi></mrow><annotation encoding="application/x-tex">d \leq n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.8304em;vertical-align:-.136em"></span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:.2778em"></span><span class="mrel">≤</span><span class="mspace" style="margin-right:.2778em"></span></span><span class="base"><span class="strut" style="height:.4306em"></span><span class="mord mathnormal">n</span></span></span></span> is the number of <em>distinct</em> integers</li></ul><h3 id="dedupe-then-sort" class="group relative"><a data-discover="true" href="#dedupe-then-sort" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Dedupe then sort permalink"/></a>Dedupe then sort</h3><p>Removing duplicates and then sorting takes <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo>+</mo><mi>d</mi><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mi>d</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n + d\log_2{d})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:.1667em"></span><span class="mop"><span class="mop">lo<span style="margin-right:.01389em">g</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:.207em"><span style="top:-2.4559em;margin-right:.05em"><span class="pstrut" style="height:2.7em"></span><span class="mtight reset-size6 size3 sizing"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:.2441em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:.1667em"></span><span class="mord"><span class="mord mathnormal">d</span></span><span class="mclose">)</span></span></span></span> time (on average) and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>d</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(d)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord mathnormal">d</span><span class="mclose">)</span></span></span></span> space because:</p><ul><li>We loop through all <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.4306em"></span><span class="mord mathnormal">n</span></span></span></span> integers and perform <a href="https://en.wikipedia.org/wiki/Time_complexity#Constant_time" rel="nofollow">constant time</a> work (on average) for each one. That takes <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span> time.</li><li>We add <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow><annotation encoding="application/x-tex">d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.6944em"></span><span class="mord mathnormal">d</span></span></span></span> integers to a set by the end of the loop. That requires <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>d</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(d)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord mathnormal">d</span><span class="mclose">)</span></span></span></span> space.</li><li>We sort the remaining deduplicated integers, which there are <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow><annotation encoding="application/x-tex">d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.6944em"></span><span class="mord mathnormal">d</span></span></span></span> of. In general sorting takes <a href="https://en.wikipedia.org/wiki/Time_complexity#Quasilinear_time" rel="nofollow">quasilinear time</a> so sorting these integers takes <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>d</mi><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mi>d</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(d\log_2{d})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:.1667em"></span><span class="mop"><span class="mop">lo<span style="margin-right:.01389em">g</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:.207em"><span style="top:-2.4559em;margin-right:.05em"><span class="pstrut" style="height:2.7em"></span><span class="mtight reset-size6 size3 sizing"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:.2441em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:.1667em"></span><span class="mord"><span class="mord mathnormal">d</span></span><span class="mclose">)</span></span></span></span> time.</li></ul><p>I used the phrase “on average” a couple of times. That’s because if the set is a <a href="https://en.wikipedia.org/wiki/Hash_table" rel="nofollow">hash table</a>, <a href="https://stackoverflow.com/a/33614512/5195839" rel="nofollow">which it is in JavaScript</a>, then insertion and lookup take linear time, not constant time, in the worst case.</p><p>That means that the algorithm takes <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mo>+</mo><mi>d</mi><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mi>d</mi><mo stretchy="false">)</mo><mo>=</mo><mi>O</mi><mo stretchy="false">(</mo><msup><mi>n</mi><mn>2</mn></msup><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n^2 + d\log_2{d}) = O(n^2)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1.0641em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:.8141em"><span style="top:-3.063em;margin-right:.05em"><span class="pstrut" style="height:2.7em"></span><span class="mtight reset-size6 size3 sizing"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mspace" style="margin-right:.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:.1667em"></span><span class="mop"><span class="mop">lo<span style="margin-right:.01389em">g</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:.207em"><span style="top:-2.4559em;margin-right:.05em"><span class="pstrut" style="height:2.7em"></span><span class="mtight reset-size6 size3 sizing"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:.2441em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:.1667em"></span><span class="mord"><span class="mord mathnormal">d</span></span><span class="mclose">)</span><span class="mspace" style="margin-right:.2778em"></span><span class="mrel">=</span><span class="mspace" style="margin-right:.2778em"></span></span><span class="base"><span class="strut" style="height:1.0641em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:.8141em"><span style="top:-3.063em;margin-right:.05em"><span class="pstrut" style="height:2.7em"></span><span class="mtight reset-size6 size3 sizing"><span class="mord mtight">2</span></span></span></span></span></span></span></span><span class="mclose">)</span></span></span></span> time in the worst case<sup><a href="#fn-2" aria-describedby="footnote-label" data-footnote-ref="" id="fnref-2">2</a></sup>.</p><h3 id="sort-then-dedupe" class="group relative"><a data-discover="true" href="#sort-then-dedupe" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Sort then dedupe permalink"/></a>Sort then dedupe</h3><p>Sorting then removing duplicates takes <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n\log_2{n})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:.1667em"></span><span class="mop"><span class="mop">lo<span style="margin-right:.01389em">g</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:.207em"><span style="top:-2.4559em;margin-right:.05em"><span class="pstrut" style="height:2.7em"></span><span class="mtight reset-size6 size3 sizing"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:.2441em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:.1667em"></span><span class="mord"><span class="mord mathnormal">n</span></span><span class="mclose">)</span></span></span></span> time and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span> space because:</p><ul><li>We sort the input integers, which there are <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.4306em"></span><span class="mord mathnormal">n</span></span></span></span> of. That takes <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n\log_2{n})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:.1667em"></span><span class="mop"><span class="mop">lo<span style="margin-right:.01389em">g</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:.207em"><span style="top:-2.4559em;margin-right:.05em"><span class="pstrut" style="height:2.7em"></span><span class="mtight reset-size6 size3 sizing"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:.2441em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:.1667em"></span><span class="mord"><span class="mord mathnormal">n</span></span><span class="mclose">)</span></span></span></span> time.</li><li>We loop through all <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.4306em"></span><span class="mord mathnormal">n</span></span></span></span> integers and perform constant time work for each one. That takes <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span> time.</li><li>We don’t allocate any space dependent on the input size. That requires only <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mn>1</mn><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(1)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord">1</span><span class="mclose">)</span></span></span></span> space.</li></ul><p>Unlike the other algorithm, the average and worst case time complexities are the same.</p><h2 id="which-is-better" class="group relative"><a data-discover="true" href="#which-is-better" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Which is better? permalink"/></a>Which is better?</h2><p>The average case is most informative, but it’s still not immediately clear whether <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo>+</mo><mi>d</mi><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mi>d</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n + d\log_2{d})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:.2222em"></span><span class="mbin">+</span><span class="mspace" style="margin-right:.2222em"></span></span><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:.1667em"></span><span class="mop"><span class="mop">lo<span style="margin-right:.01389em">g</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:.207em"><span style="top:-2.4559em;margin-right:.05em"><span class="pstrut" style="height:2.7em"></span><span class="mtight reset-size6 size3 sizing"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:.2441em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:.1667em"></span><span class="mord"><span class="mord mathnormal">d</span></span><span class="mclose">)</span></span></span></span> is more efficient than <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n\log_2{n})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:.1667em"></span><span class="mop"><span class="mop">lo<span style="margin-right:.01389em">g</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:.207em"><span style="top:-2.4559em;margin-right:.05em"><span class="pstrut" style="height:2.7em"></span><span class="mtight reset-size6 size3 sizing"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:.2441em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:.1667em"></span><span class="mord"><span class="mord mathnormal">n</span></span><span class="mclose">)</span></span></span></span> because we have two variables, not one. Let’s explore what happens when we assume different relationships between <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.4306em"></span><span class="mord mathnormal">n</span></span></span></span> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow><annotation encoding="application/x-tex">d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.6944em"></span><span class="mord mathnormal">d</span></span></span></span>.</p><h3 id="d-is-significantly-smaller-than-n" class="group relative"><a data-discover="true" href="#d-is-significantly-smaller-than-n" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;d&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;d&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:.6944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;d&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; is &lt;em&gt;significantly&lt;/em&gt; smaller than &lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;n&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:.4306em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; permalink"/></a><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow><annotation encoding="application/x-tex">d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.6944em"></span><span class="mord mathnormal">d</span></span></span></span> is <em>significantly</em> smaller than <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.4306em"></span><span class="mord mathnormal">n</span></span></span></span></h3><p>If <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow><annotation encoding="application/x-tex">d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.6944em"></span><span class="mord mathnormal">d</span></span></span></span> is <em>significantly</em> smaller than <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.4306em"></span><span class="mord mathnormal">n</span></span></span></span>, meaning there are many duplicate integers, then the time complexity of removing duplicates and then sorting becomes <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>, which is certainly more efficient than <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n\log_2{n})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:.1667em"></span><span class="mop"><span class="mop">lo<span style="margin-right:.01389em">g</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:.207em"><span style="top:-2.4559em;margin-right:.05em"><span class="pstrut" style="height:2.7em"></span><span class="mtight reset-size6 size3 sizing"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:.2441em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:.1667em"></span><span class="mord"><span class="mord mathnormal">n</span></span><span class="mclose">)</span></span></span></span>.</p><p>There’s still the matter of the <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>d</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(d)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord mathnormal">d</span><span class="mclose">)</span></span></span></span> space complexity, but that’s unlikely to be a problem unless <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow><annotation encoding="application/x-tex">d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.6944em"></span><span class="mord mathnormal">d</span></span></span></span>, and consequently <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.4306em"></span><span class="mord mathnormal">n</span></span></span></span>, are very large.</p><h3 id="d-approx-n" class="group relative"><a data-discover="true" href="#d-approx-n" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="&lt;span class=&quot;katex&quot;&gt;&lt;span class=&quot;katex-mathml&quot;&gt;&lt;math xmlns=&quot;http://www.w3.org/1998/Math/MathML&quot;&gt;&lt;semantics&gt;&lt;mrow&gt;&lt;mi&gt;d&lt;/mi&gt;&lt;mo&gt;≈&lt;/mo&gt;&lt;mi&gt;n&lt;/mi&gt;&lt;/mrow&gt;&lt;annotation encoding=&quot;application/x-tex&quot;&gt;d \approx n&lt;/annotation&gt;&lt;/semantics&gt;&lt;/math&gt;&lt;/span&gt;&lt;span class=&quot;katex-html&quot; aria-hidden=&quot;true&quot;&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:.6944em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;d&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:.2778em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mrel&quot;&gt;≈&lt;/span&gt;&lt;span class=&quot;mspace&quot; style=&quot;margin-right:.2778em&quot;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;base&quot;&gt;&lt;span class=&quot;strut&quot; style=&quot;height:.4306em&quot;&gt;&lt;/span&gt;&lt;span class=&quot;mord mathnormal&quot;&gt;n&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; permalink"/></a><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><mo>≈</mo><mi>n</mi></mrow><annotation encoding="application/x-tex">d \approx n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.6944em"></span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:.2778em"></span><span class="mrel">≈</span><span class="mspace" style="margin-right:.2778em"></span></span><span class="base"><span class="strut" style="height:.4306em"></span><span class="mord mathnormal">n</span></span></span></span></h3><p>If <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi></mrow><annotation encoding="application/x-tex">d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.6944em"></span><span class="mord mathnormal">d</span></span></span></span> and <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi></mrow><annotation encoding="application/x-tex">n</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.4306em"></span><span class="mord mathnormal">n</span></span></span></span> are <em>not</em> significantly different, meaning there are few or no duplicate integers, then the time complexity of both algorithms becomes <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n\log_2{n})</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:.1667em"></span><span class="mop"><span class="mop">lo<span style="margin-right:.01389em">g</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:.207em"><span style="top:-2.4559em;margin-right:.05em"><span class="pstrut" style="height:2.7em"></span><span class="mtight reset-size6 size3 sizing"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:.2441em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:.1667em"></span><span class="mord"><span class="mord mathnormal">n</span></span><span class="mclose">)</span></span></span></span>.</p><p>However, removing duplicates and then sorting also requires <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>d</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(d)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord mathnormal">d</span><span class="mclose">)</span></span></span></span> space, which with our assumption is approximately <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>O</mi><mo stretchy="false">(</mo><mi>n</mi><mo stretchy="false">)</mo></mrow><annotation encoding="application/x-tex">O(n)</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:1em;vertical-align:-.25em"></span><span class="mord mathnormal" style="margin-right:.02778em">O</span><span class="mopen">(</span><span class="mord mathnormal">n</span><span class="mclose">)</span></span></span></span>, that the other algorithm doesn’t. Plus, in practice that space also takes additional time to allocate, insert into, and query, even if it doesn’t affect the time complexity.</p><p>So in this case sorting and then removing duplicates is more efficient.</p><h2 id="which-is-better-empirically" class="group relative"><a data-discover="true" href="#which-is-better-empirically" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Which is better &lt;em&gt;empirically&lt;/em&gt;? permalink"/></a>Which is better <em>empirically</em>?</h2><p>Our theoretical analysis suggests that we should sort and then remove duplicates if we expect few duplicates. Otherwise, we should remove duplicates and then sort. But is that empirically true? And if so, then how many duplicates does it take for switching algorithms to be worthwhile?</p><p><a href="https://github.com/TomerAberbach/to-dedupe-then-sort-or-sort-then-dedupe" rel="nofollow">I benchmarked to find out!</a> I ran each algorithm on a shuffled array of 100,000 integers with various duplicate integer percentages and this was the result:</p><p><img alt="" src="/assets/efficiency-of-sorting-and-deduping-CRzCd3Bv.svg"/></p><p>This confirms that the overhead from the set used when removing duplicates and then sorting isn’t <em>always</em> offset by the reduced number of integers to sort later<sup><a href="#fn-3" aria-describedby="footnote-label" data-footnote-ref="" id="fnref-3">3</a></sup>.</p><p>In the case of 100,000 integers the overhead is only offset once roughly 27% of the integers are duplicates. I also benchmarked some other array sizes and the boundary varies. For example, for 100 integers the boundary was around 10%. Do your own benchmarking if you’re going to depend on this!</p><h2 id="conclusion" class="group relative"><a data-discover="true" href="#conclusion" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Conclusion permalink"/></a>Conclusion</h2><p>The appropriate approach depends on the expected percentage of duplicates. And problems that seem simple… aren’t.</p><section class="footnotes" data-footnotes=""><h2 id="footnote-label" class="sr-only">Footnotes</h2><ol><li id="fn-1"><p>The sorting algorithms built into programming languages are meticulously optimized by being written low-level programming languages and using a state of the art algorithm like <a href="https://en.wikipedia.org/wiki/Timsort" rel="nofollow">Timsort</a>. <a href="#fnref-1" aria-label="Back to reference 1" class="data-footnote-backref">↩</a></p></li><li id="fn-2"><p><span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><msup><mi>n</mi><mn>2</mn></msup></mrow><annotation encoding="application/x-tex">n^2</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.8141em"></span><span class="mord"><span class="mord mathnormal">n</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:.8141em"><span style="top:-3.063em;margin-right:.05em"><span class="pstrut" style="height:2.7em"></span><span class="mtight reset-size6 size3 sizing"><span class="mord mtight">2</span></span></span></span></span></span></span></span></span></span></span> asymptotically dominates <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mi>d</mi></mrow><annotation encoding="application/x-tex">d\log_2{d}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.9386em;vertical-align:-.2441em"></span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:.1667em"></span><span class="mop"><span class="mop">lo<span style="margin-right:.01389em">g</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:.207em"><span style="top:-2.4559em;margin-right:.05em"><span class="pstrut" style="height:2.7em"></span><span class="mtight reset-size6 size3 sizing"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:.2441em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:.1667em"></span><span class="mord"><span class="mord mathnormal">d</span></span></span></span></span> because <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>n</mi><mo>≥</mo><mi>d</mi></mrow><annotation encoding="application/x-tex">n \geq d</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.7719em;vertical-align:-.136em"></span><span class="mord mathnormal">n</span><span class="mspace" style="margin-right:.2778em"></span><span class="mrel">≥</span><span class="mspace" style="margin-right:.2778em"></span></span><span class="base"><span class="strut" style="height:.6944em"></span><span class="mord mathnormal">d</span></span></span></span>, so we can omit <span class="katex"><span class="katex-mathml"><math xmlns="http://www.w3.org/1998/Math/MathML"><semantics><mrow><mi>d</mi><msub><mrow><mi>log</mi><mo>⁡</mo></mrow><mn>2</mn></msub><mi>d</mi></mrow><annotation encoding="application/x-tex">d\log_2{d}</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="base"><span class="strut" style="height:.9386em;vertical-align:-.2441em"></span><span class="mord mathnormal">d</span><span class="mspace" style="margin-right:.1667em"></span><span class="mop"><span class="mop">lo<span style="margin-right:.01389em">g</span></span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:.207em"><span style="top:-2.4559em;margin-right:.05em"><span class="pstrut" style="height:2.7em"></span><span class="mtight reset-size6 size3 sizing"><span class="mord mtight">2</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:.2441em"><span></span></span></span></span></span></span><span class="mspace" style="margin-right:.1667em"></span><span class="mord"><span class="mord mathnormal">d</span></span></span></span></span>. <a href="#fnref-2" aria-label="Back to reference 2" class="data-footnote-backref">↩</a></p></li><li id="fn-3"><p>You might be wondering why sorting and then removing duplicates becomes so much more efficient when all integers in the input array are the same (100% duplicates). I don’t know for certain, but I would guess the JavaScript sorting algorithm has a linear time optimization that skips sorting when the values are already in order, which is the case if all the values are the same! <a href="#fnref-3" aria-label="Back to reference 3" class="data-footnote-backref">↩</a></p></li></ol></section>]]></content:encoded>
                <description><![CDATA[I recently came across a deceptively simple problem. I wanted to dedupe and sort a list of integers in-place. My initial instinct was to first remove duplicates and then sort using the programming…]]></description>
                <guid isPermaLink="true">https://tomeraberba.ch/to-dedupe-then-sort-or-sort-then-dedupe</guid>
                <pubDate>Sun, 30 Jun 2024 00:00:00 GMT</pubDate>
                
              </item>
            
              <item>
                <title><![CDATA[The 29 Days per Year Bug (30 Days for Leap Years!)]]></title>
                <link>https://tomeraberba.ch/the-29-days-per-year-bug</link>
                <category><![CDATA[bugs]]></category><category><![CDATA[code]]></category><category><![CDATA[dates]]></category><category><![CDATA[docs]]></category><category><![CDATA[engineering]]></category><category><![CDATA[google]]></category>
                <content:encoded><![CDATA[<p>A few years ago my coworker was working on a Google Docs feature when they came across a <em>bewildering</em> bug.</p><h2 id="the-feature-" class="group relative"><a data-discover="true" href="#the-feature-" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="The feature &lt;span aria-label=&quot;calendar&quot; role=&quot;img&quot;&gt;📅&lt;/span&gt; permalink"/></a>The feature <span aria-label="calendar" role="img">📅</span></h2><p>For one component of this feature, users were able to pick any date, save it, and see it displayed later. This part was implemented and working fine for a couple weeks.</p><h2 id="the-bug-report-" class="group relative"><a data-discover="true" href="#the-bug-report-" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="The bug report &lt;span aria-label=&quot;bug&quot; role=&quot;img&quot;&gt;🐛&lt;/span&gt; permalink"/></a>The bug report <span aria-label="bug" role="img">🐛</span></h2><p>Another engineer noticed that a date they had entered as September 1, and which had previously been displaying correctly, was now being displayed as <em>October</em> 1. They confirmed that the date was correctly stored in our database and correctly sent from the server to the client. So my coworker concluded there was a bug in displaying the date on the client.</p><h2 id="the-unit-test-" class="group relative"><a data-discover="true" href="#the-unit-test-" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="The unit test &lt;span aria-label=&quot;test tube&quot; role=&quot;img&quot;&gt;🧪&lt;/span&gt; permalink"/></a>The unit test <span aria-label="test tube" role="img">🧪</span></h2><p>My coworker successfully wrote a <a href="https://en.wikipedia.org/wiki/Test-driven_development" rel="nofollow">failing unit test</a> that resulted in displaying October 1 for an input date of September 1. They considered debugging further, but the bug was reported late in the day, so they decided to log off and debug further the next day.</p><p>Little did they know that the next day the unit test would start passing! They could not reproduce the unit test failure no matter what they did. And to top it all off, September 1 was now displaying correctly for the bug reporter.</p><h2 id="eureka-" class="group relative"><a data-discover="true" href="#eureka-" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Eureka! &lt;span aria-label=&quot;light bulb&quot; role=&quot;img&quot;&gt;💡&lt;/span&gt; permalink"/></a>Eureka! <span aria-label="light bulb" role="img">💡</span></h2><p>It was time to get the team together to figure this out. Several of us sat down and scrutinized the code until the root cause finally dawned on us. We had a function that converted server provided dates to dates for use in the UI. It looked something like this:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> toDate </span><span style="color:#89ddff">=</span><span style="color:#babed8;font-style:italic"> serverDate</span><span style="color:#c792ea"> =&gt;</span><span style="color:#89ddff"> {</span></span>
<span class="line"><span style="color:#c792ea">  const</span><span style="color:#babed8"> date</span><span style="color:#89ddff"> =</span><span style="color:#89ddff"> new</span><span style="color:#82aaff"> Date</span><span style="color:#f07178">()</span></span>
<span class="line"><span style="color:#babed8">  date</span><span style="color:#89ddff">.</span><span style="color:#82aaff">setFullYear</span><span style="color:#f07178">(</span><span style="color:#babed8">serverDate</span><span style="color:#89ddff">.</span><span style="color:#82aaff">getYear</span><span style="color:#f07178">())</span></span>
<span class="line"><span style="color:#babed8">  date</span><span style="color:#89ddff">.</span><span style="color:#82aaff">setMonth</span><span style="color:#f07178">(</span><span style="color:#babed8">serverDate</span><span style="color:#89ddff">.</span><span style="color:#82aaff">getMonth</span><span style="color:#f07178">())</span></span>
<span class="line"><span style="color:#babed8">  date</span><span style="color:#89ddff">.</span><span style="color:#82aaff">setDate</span><span style="color:#f07178">(</span><span style="color:#babed8">serverDate</span><span style="color:#89ddff">.</span><span style="color:#82aaff">getDay</span><span style="color:#f07178">())</span></span>
<span class="line"><span style="color:#89ddff;font-style:italic">  return</span><span style="color:#babed8"> date</span></span>
<span class="line"><span style="color:#89ddff">}</span></span></code></pre><p>Do you see the problem?</p><p>We realized that the bug was reported on August 31 and disappeared on September 1, the next month. Why does that matter? <code>new Date()</code> creates a date object for the <em>current</em> date and time, and it turns out that calling <code>setMonth</code> on the object does not reset the object’s day. It keeps the same day… but only if it can! <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMonth#description" rel="nofollow">If the current day of the month is greater than the number of days in the month passed to <code>setMonth</code>, then the days will “overflow” into the next month</a>.</p><p>That’s why September 1 displayed as October 1 on August 31:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> toDate </span><span style="color:#89ddff">=</span><span style="color:#babed8"> (</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">  // September 1, 2021</span></span>
<span class="line"><span style="color:#babed8;font-style:italic">  serverDate</span><span style="color:#89ddff">,</span></span>
<span class="line"><span style="color:#babed8">) </span><span style="color:#c792ea">=&gt;</span><span style="color:#89ddff"> {</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">  // August 31, 2021</span></span>
<span class="line"><span style="color:#c792ea">  const</span><span style="color:#babed8"> date</span><span style="color:#89ddff"> =</span><span style="color:#89ddff"> new</span><span style="color:#82aaff"> Date</span><span style="color:#f07178">()</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">  // Still August 31, 2021</span></span>
<span class="line"><span style="color:#babed8">  date</span><span style="color:#89ddff">.</span><span style="color:#82aaff">setFullYear</span><span style="color:#f07178">(</span><span style="color:#babed8">serverDate</span><span style="color:#89ddff">.</span><span style="color:#82aaff">getYear</span><span style="color:#f07178">())</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">  // September 31 -&gt; OVERFLOW! (September has 30 days) -&gt; October 1</span></span>
<span class="line"><span style="color:#babed8">  date</span><span style="color:#89ddff">.</span><span style="color:#82aaff">setMonth</span><span style="color:#f07178">(</span><span style="color:#babed8">serverDate</span><span style="color:#89ddff">.</span><span style="color:#82aaff">getMonth</span><span style="color:#f07178">())</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">  // Still October 1</span></span>
<span class="line"><span style="color:#babed8">  date</span><span style="color:#89ddff">.</span><span style="color:#82aaff">setDate</span><span style="color:#f07178">(</span><span style="color:#babed8">serverDate</span><span style="color:#89ddff">.</span><span style="color:#82aaff">getDay</span><span style="color:#f07178">())</span></span>
<span class="line"><span style="color:#89ddff;font-style:italic">  return</span><span style="color:#babed8"> date</span></span>
<span class="line"><span style="color:#89ddff">}</span></span></code></pre><p>But the following day, September 1, didn’t have the same problem because there was no overflow. The fix was simple:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> toDate </span><span style="color:#89ddff">=</span><span style="color:#babed8;font-style:italic"> serverDate</span><span style="color:#c792ea"> =&gt;</span></span>
<span class="line"><span style="color:#89ddff">  new</span><span style="color:#82aaff"> Date</span><span style="color:#babed8">(serverDate</span><span style="color:#89ddff">.</span><span style="color:#82aaff">getYear</span><span style="color:#babed8">()</span><span style="color:#89ddff">,</span><span style="color:#babed8"> serverDate</span><span style="color:#89ddff">.</span><span style="color:#82aaff">getMonth</span><span style="color:#babed8">()</span><span style="color:#89ddff">,</span><span style="color:#babed8"> serverDate</span><span style="color:#89ddff">.</span><span style="color:#82aaff">getDay</span><span style="color:#babed8">())</span></span></code></pre><h2 id="why-29-or-30-days-per-year-" class="group relative"><a data-discover="true" href="#why-29-or-30-days-per-year-" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Why 29 (or 30) days per year? &lt;span aria-label=&quot;thinking face&quot; role=&quot;img&quot;&gt;🤔&lt;/span&gt; permalink"/></a>Why 29 (or 30) days per year? <span aria-label="thinking face" role="img">🤔</span></h2><p>So why can this bug only happen 29 (or 30) days per year? Well, the month with the lowest number of days is February, with 28 days on <a href="https://en.wikipedia.org/wiki/Common_year" rel="nofollow">common years</a>. That means displaying a common year February date maximizes the potential for date overflow; the bug will occur whenever the <em>current</em> date’s day of the month is greater than 28. But how often does that happen? How many dates have a day of the month greater than 28?</p><div class="overflow-auto"><table><thead><tr><th>Month</th><th>Days</th><th>Days of the month &gt; 28</th></tr></thead><tbody><tr><td>January</td><td>31</td><td>3</td></tr><tr><td>February</td><td>28 (common years) or 29 (leap years)</td><td>0 (common years) or 1 (leap years)</td></tr><tr><td>March</td><td>31</td><td>3</td></tr><tr><td>April</td><td>30</td><td>2</td></tr><tr><td>May</td><td>31</td><td>3</td></tr><tr><td>June</td><td>30</td><td>2</td></tr><tr><td>July</td><td>31</td><td>3</td></tr><tr><td>August</td><td>31</td><td>3</td></tr><tr><td>September</td><td>30</td><td>2</td></tr><tr><td>October</td><td>31</td><td>3</td></tr><tr><td>November</td><td>30</td><td>2</td></tr><tr><td>December</td><td>31</td><td>3</td></tr></tbody></table></div><p>Sum up the last column and we have our answer! This bug can happen on only 29 days for common years and 30 days for <a href="https://en.wikipedia.org/wiki/Leap_year" rel="nofollow">leap years</a>.</p><h2 id="lessons-learned-" class="group relative"><a data-discover="true" href="#lessons-learned-" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Lessons learned &lt;span aria-label=&quot;school&quot; role=&quot;img&quot;&gt;🏫&lt;/span&gt; permalink"/></a>Lessons learned <span aria-label="school" role="img">🏫</span></h2><p>I was thinking about this bug for days (ha!) after solving it and reached a few conclusions:</p><ul><li><p>Don’t assume a constructor has the same behavior as a sequence of setter calls for the same fields! A constructor has access to all the inputs at once while each setter only has isolated access to each input. That can force some odd behaviors.</p></li><li><p>Don’t underestimate the complexity of dates and times. If it involves dates and times, then test it thoroughly, even if it seems trivial. I guarantee you it’s not!</p></li><li><p>Teamwork makes the dream work <span aria-label="smiling face with heart-eyes" role="img">😍</span></p></li></ul>]]></content:encoded>
                <description><![CDATA[A few years ago my coworker was working on a Google Docs feature when they came across a bewildering bug. The feature 📅 For one component of this feature, users were able to pick any date, save it,…]]></description>
                <guid isPermaLink="true">https://tomeraberba.ch/the-29-days-per-year-bug</guid>
                <pubDate>Tue, 23 Apr 2024 00:00:00 GMT</pubDate>
                
              </item>
            
              <item>
                <title><![CDATA[Leverage]]></title>
                <link>https://tomeraberba.ch/leverage</link>
                <category><![CDATA[jazz]]></category><category><![CDATA[music]]></category><category><![CDATA[piano]]></category><category><![CDATA[tracks]]></category>
                <content:encoded><![CDATA[<p>I composed, produced, and released an instrumental jazz track!</p><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowFullScreen="" frameBorder="0" height="150" src="https://www.youtube.com/embed/mOxsT5-jnc8?feature=oembed" title="Leverage" width="200"></iframe><p>You can also find the track on <a href="https://open.spotify.com/track/4vGt77KgylifG6aawGa3sM" rel="nofollow">Spotify</a>, <a href="https://music.apple.com/us/album/leverage-single/1729510141" rel="nofollow">Apple Music</a>, <a href="https://music.youtube.com/watch?v=mOxsT5-jnc8" rel="nofollow">YouTube Music</a>, and other music streaming services. Special thanks to <a href="https://www.instagram.com/happy_lil_paintings" rel="nofollow">Tiana Rogers</a> for the album cover!</p>]]></content:encoded>
                <description><![CDATA[I composed, produced, and released an instrumental jazz track! https://www.youtube.com/watch?v=mOxsT5-jnc8 You can also find the track on Spotify, Apple Music, YouTube Music, and other music streaming…]]></description>
                <guid isPermaLink="true">https://tomeraberba.ch/leverage</guid>
                <pubDate>Mon, 26 Feb 2024 00:00:00 GMT</pubDate>
                
              </item>
            
              <item>
                <title><![CDATA[When Two Bugs Cancel Out]]></title>
                <link>https://tomeraberba.ch/when-two-bugs-cancel-out</link>
                <category><![CDATA[bugs]]></category><category><![CDATA[code]]></category><category><![CDATA[docs]]></category><category><![CDATA[engineering]]></category><category><![CDATA[google]]></category>
                <content:encoded><![CDATA[<p>I encountered a perplexing bug while implementing <a href="https://workspaceupdates.googleblog.com/2022/10/split-table-cells-in-google-docs.html" rel="nofollow">table cell splitting in Google Docs</a>.</p><h2 id="the-bug-report-" class="group relative"><a data-discover="true" href="#the-bug-report-" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="The bug report &lt;span aria-label=&quot;bug&quot; role=&quot;img&quot;&gt;🐛&lt;/span&gt; permalink"/></a>The bug report <span aria-label="bug" role="img">🐛</span></h2><p>The QA team found a bug where some of a table’s borders would be missing after a table cell split in certain scenarios. However, the borders reappeared when refreshing the page. That meant the document’s data was likely stored correctly and that <a href="https://en.wikipedia.org/wiki/Incremental_computing" rel="nofollow">incremental</a> layout or rendering had a bug.</p><h2 id="the-investigation-️" class="group relative"><a data-discover="true" href="#the-investigation-️" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="The investigation &lt;span aria-label=&quot;detective&quot; role=&quot;img&quot;&gt;🕵️&lt;/span&gt; permalink"/></a>The investigation <span aria-label="detective" role="img">🕵️</span></h2><p>At the time, the team was <a href="https://workspaceupdates.googleblog.com/2021/05/Google-Docs-Canvas-Based-Rendering-Update.html" rel="nofollow">migrating Google Docs from HTML-based rendering to canvas-based rendering</a>. QA reported the bug about a canvas-rendered document, which made me suspect it was a bug in canvas-based rendering. So I tried to reproduce the issue on an HTML-rendered document and it didn’t reproduce!</p><p>At this point I concluded the new canvas-based rendering must have a bug. Oh how wrong I was… After <em>much</em> investigation, I realized canvas-based rendering was correct.</p><h2 id="two-bugs-" class="group relative"><a data-discover="true" href="#two-bugs-" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Two bugs &lt;span aria-label=&quot;bug&quot; role=&quot;img&quot;&gt;🐛&lt;/span&gt;&lt;span aria-label=&quot;beetle&quot; role=&quot;img&quot;&gt;🪲&lt;/span&gt; permalink"/></a>Two bugs <span aria-label="bug" role="img">🐛</span><span aria-label="beetle" role="img">🪲</span></h2><p>In Google Docs, the output of layout is the input for rendering. It turned out layout had a bug and canvas-based rendering was correctly displaying layout’s incorrect output. So why did HTML-based rendering look correct? It turned out that HTML-based rendering <strong><em>also</em></strong> had a bug, and the bug coincidentally “fixed” layout’s output so that the final output looked correct.</p><svg aria-roledescription="flowchart-v2" class="flowchart" id="mermaid-0" style="font-family:Kantumruy Pro;font-size:16px;fill:#333;max-width:580.765625px" viewBox="0 0 580.766 278" width="100%" xmlns="http://www.w3.org/2000/svg"><style>@keyframes edge-animation-frame{0%{stroke-dashoffset:0}}@keyframes dash{to{stroke-dashoffset:0}}#mermaid-0 .edge-thickness-normal{stroke-width:1px}#mermaid-0 .edge-pattern-solid{stroke-dasharray:0}#mermaid-0 .marker{fill:#0b0b0b;stroke:#0b0b0b}#mermaid-0 .marker.cross{stroke:#0b0b0b}#mermaid-0 svg{font-family:Kantumruy Pro;font-size:16px}#mermaid-0 p{margin:0}#mermaid-0 .label{font-family:Kantumruy Pro;color:#333}#mermaid-0 span{fill:#333;color:#333}#mermaid-0 .node circle,#mermaid-0 .node path,#mermaid-0 .node rect{fill:#fff4dd;stroke:#edb;stroke-width:1px}#mermaid-0 .node .label{text-align:center}#mermaid-0 .flowchart-link{stroke:#0b0b0b;fill:none}#mermaid-0 .edgeLabel{background-color:#fdd;text-align:center}#mermaid-0 .edgeLabel p{background-color:#fdd}#mermaid-0 .edgeLabel rect{opacity:.5;background-color:#fdd;fill:#fdd}#mermaid-0 .labelBkg{background-color:rgba(244,221,255,.5)}#mermaid-0 :root{--mermaid-font-family:Kantumruy Pro}</style><marker class="flowchart-v2 marker" id="mermaid-0_flowchart-v2-pointEnd" markerHeight="8" markerUnits="userSpaceOnUse" markerWidth="8" orient="auto" refX="5" refY="5" viewBox="0 0 10 10"><path class="arrowMarkerPath" d="m0 0 10 5-10 5z" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker" id="mermaid-0_flowchart-v2-pointStart" markerHeight="8" markerUnits="userSpaceOnUse" markerWidth="8" orient="auto" refX="4.5" refY="5" viewBox="0 0 10 10"><path class="arrowMarkerPath" d="m0 5 10 5V0z" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker" id="mermaid-0_flowchart-v2-circleEnd" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="11" refY="5" viewBox="0 0 10 10"><circle class="arrowMarkerPath" cx="5" cy="5" r="5" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker class="flowchart-v2 marker" id="mermaid-0_flowchart-v2-circleStart" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="-1" refY="5" viewBox="0 0 10 10"><circle class="arrowMarkerPath" cx="5" cy="5" r="5" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker class="flowchart-v2 marker cross" id="mermaid-0_flowchart-v2-crossEnd" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="12" refY="5.2" viewBox="0 0 11 11"><path class="arrowMarkerPath" d="m1 1 9 9m0-9-9 9" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker cross" id="mermaid-0_flowchart-v2-crossStart" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="-1" refY="5.2" viewBox="0 0 11 11"><path class="arrowMarkerPath" d="m1 1 9 9m0-9-9 9" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><g class="root"><g class="edgePaths"><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="m225.316 60.305-13.111 4.449c-13.111 4.449-39.334 13.348-52.445 21.297C146.648 94 146.648 101 146.648 104.5v3.5" data-edge="true" data-et="edge" data-id="L_L_HR_0" data-points="W3sieCI6MjI1LjMxNjQwNjI1LCJ5Ijo2MC4zMDQ3NDkwMzc3MDE3fSx7IngiOjE0Ni42NDg0Mzc1LCJ5Ijo4N30seyJ4IjoxNDYuNjQ4NDM3NSwieSI6MTEyfV0=" id="L_L_HR_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="m374.457 60.305 13.111 4.449c13.112 4.449 39.334 13.348 52.446 21.297S453.125 101 453.125 104.5v3.5" data-edge="true" data-et="edge" data-id="L_L_CR_0" data-points="W3sieCI6Mzc0LjQ1NzAzMTI1LCJ5Ijo2MC4zMDQ3NDkwMzc3MDE3fSx7IngiOjQ1My4xMjUsInkiOjg3fSx7IngiOjQ1My4xMjUsInkiOjExMn1d" id="L_L_CR_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M146.648 166v46" data-edge="true" data-et="edge" data-id="L_HR_HO_0" data-points="W3sieCI6MTQ2LjY0ODQzNzUsInkiOjE2Nn0seyJ4IjoxNDYuNjQ4NDM3NSwieSI6MTkxfSx7IngiOjE0Ni42NDg0Mzc1LCJ5IjoyMTZ9XQ==" id="L_HR_HO_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M453.125 166v46" data-edge="true" data-et="edge" data-id="L_CR_CO_0" data-points="W3sieCI6NDUzLjEyNSwieSI6MTY2fSx7IngiOjQ1My4xMjUsInkiOjE5MX0seyJ4Ijo0NTMuMTI1LCJ5IjoyMTZ9XQ==" id="L_CR_CO_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path></g><g class="edgeLabels"><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_L_HR_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_L_CR_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_HR_HO_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_CR_CO_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="nodes"><g class="default node" id="flowchart-L-0"><path class="basic label-container" d="M225.317 8h149.14v54h-149.14z"></path><g class="label" transform="translate(255.316 23)"><rect></rect><foreignObject height="24" width="89.141"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>layout (bug)</p></span></div></foreignObject></g></g><g class="default node" id="flowchart-HR-1"><path class="basic label-container" d="M9.812 112h273.672v54H9.812z"></path><g class="label" transform="translate(39.813 127)"><rect></rect><foreignObject height="24" width="213.672"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>HTML-based rendering (bug)</p></span></div></foreignObject></g></g><g class="default node" id="flowchart-CR-2"><path class="basic label-container" d="M333.484 112h239.281v54H333.484z"></path><g class="label" transform="translate(363.484 127)"><rect></rect><foreignObject height="24" width="179.281"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>canvas-based rendering</p></span></div></foreignObject></g></g><g class="default node" id="flowchart-HO-3"><path class="basic label-container" d="M8 216h277.297v54H8z"></path><g class="label" transform="translate(38 231)"><rect></rect><foreignObject height="24" width="217.297"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>coincidentally correct output</p></span></div></foreignObject></g></g><g class="default node" id="flowchart-CO-4"><path class="basic label-container" d="M361.719 216h182.813v54H361.719z"></path><g class="label" transform="translate(391.719 231)"><rect></rect><foreignObject height="24" width="122.813"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>incorrect output</p></span></div></foreignObject></g></g></g></g></svg><h2 id="how-" class="group relative"><a data-discover="true" href="#how-" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="How? &lt;span aria-label=&quot;thinking face&quot; role=&quot;img&quot;&gt;🤔&lt;/span&gt; permalink"/></a>How? <span aria-label="thinking face" role="img">🤔</span></h2><p>So how did the HTML-based rendering bug cancel out the layout bug? One of the reasons the team switched to canvas-based rendering, other than better performance, is that it’s more precise than HTML-based rendering.</p><p>With canvas-based rendering you effectively have to specify the location of every pixel, which means it can accurately render any layout. It did just that for the incorrect layout output!</p><p>On the other hand, with HTML-based rendering a table is rendered using a <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table" rel="nofollow"><code>table</code> element</a>, which supports a limited set of layouts. HTML-based rendering could not faithfully render the incorrect layout output so it rendered the closest thing, which coincidentally looked correct.</p>]]></content:encoded>
                <description><![CDATA[I encountered a perplexing bug while implementing table cell splitting in Google Docs. The bug report 🐛 The QA team found a bug where some of a table's borders would be missing after a table cell…]]></description>
                <guid isPermaLink="true">https://tomeraberba.ch/when-two-bugs-cancel-out</guid>
                <pubDate>Mon, 18 Sep 2023 00:00:00 GMT</pubDate>
                
              </item>
            
              <item>
                <title><![CDATA[The Making of Keyalesce]]></title>
                <link>https://tomeraberba.ch/the-making-of-keyalesce</link>
                <category><![CDATA[code]]></category><category><![CDATA[data structures]]></category><category><![CDATA[gc]]></category><category><![CDATA[javascript]]></category><category><![CDATA[libraries]]></category><category><![CDATA[performance]]></category><category><![CDATA[tries]]></category><category><![CDATA[typescript]]></category>
                <content:encoded><![CDATA[<p>Have you ever wanted to use <a href="https://www.typescriptlang.org/docs/handbook/2/objects.html#tuple-types" rel="nofollow">tuples</a> or objects for the keys of a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map" rel="nofollow"><code>Map</code></a> or the values of a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set" rel="nofollow"><code>Set</code></a>? It’s a <a href="https://stackoverflow.com/questions/43592760/typescript-javascript-using-tuple-as-key-of-map" rel="nofollow">very</a> <a href="https://stackoverflow.com/questions/21838436/map-using-tuples-or-objects" rel="nofollow">common</a> <a href="https://stackoverflow.com/questions/63179867/set-of-tuples-in-javascript" rel="nofollow">question</a> because the following code doesn’t do what you might expect:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> map </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> new</span><span style="color:#82aaff"> Map</span><span style="color:#babed8">()</span></span>
<span class="line"><span style="color:#babed8">map</span><span style="color:#89ddff">.</span><span style="color:#82aaff">set</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#babed8">]</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 3</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(map</span><span style="color:#89ddff">.</span><span style="color:#82aaff">get</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#babed8">]))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; undefined</span></span>
<span class="line"></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> set </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> new</span><span style="color:#82aaff"> Set</span><span style="color:#babed8">()</span></span>
<span class="line"><span style="color:#babed8">set</span><span style="color:#89ddff">.</span><span style="color:#82aaff">add</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#babed8">])</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(set</span><span style="color:#89ddff">.</span><span style="color:#82aaff">has</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#babed8">]))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false</span></span></code></pre><p>The code behaves this way because a <code>Map</code>’s keys and a <code>Set</code>’s values are compared using reference equality, as specified by the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#key_equality" rel="nofollow">SameValueZero algorithm</a>. Two deeply equal arrays are not <em>referentially</em> equal:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#babed8">] </span><span style="color:#89ddff">===</span><span style="color:#babed8"> [</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#babed8">])</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false</span></span></code></pre><p>The following code behaves more predictably:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#676e95;font-style:italic">// A reference to a single array instance!</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> tuple </span><span style="color:#89ddff">=</span><span style="color:#babed8"> [</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#babed8">]</span></span>
<span class="line"></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> map </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> new</span><span style="color:#82aaff"> Map</span><span style="color:#babed8">()</span></span>
<span class="line"><span style="color:#babed8">map</span><span style="color:#89ddff">.</span><span style="color:#82aaff">set</span><span style="color:#babed8">(tuple</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 3</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(map</span><span style="color:#89ddff">.</span><span style="color:#82aaff">get</span><span style="color:#babed8">(tuple))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; 3</span></span>
<span class="line"></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> set </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> new</span><span style="color:#82aaff"> Set</span><span style="color:#babed8">()</span></span>
<span class="line"><span style="color:#babed8">set</span><span style="color:#89ddff">.</span><span style="color:#82aaff">add</span><span style="color:#babed8">(tuple)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(set</span><span style="color:#89ddff">.</span><span style="color:#82aaff">has</span><span style="color:#babed8">(tuple))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span></code></pre><p>But that isn’t particularly useful because typically you’re constructing tuples on the fly using values from some external source:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> f </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> (</span><span style="color:#babed8;font-style:italic">x</span><span style="color:#89ddff">,</span><span style="color:#babed8;font-style:italic"> y</span><span style="color:#89ddff">)</span><span style="color:#c792ea"> =&gt;</span><span style="color:#89ddff"> {</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">  // This will return undefined because `[x, y]` is a new array!</span></span>
<span class="line"><span style="color:#c792ea">  const</span><span style="color:#babed8"> value</span><span style="color:#89ddff"> =</span><span style="color:#babed8"> map</span><span style="color:#89ddff">.</span><span style="color:#82aaff">get</span><span style="color:#f07178">([</span><span style="color:#babed8">x</span><span style="color:#89ddff">,</span><span style="color:#babed8"> y</span><span style="color:#f07178">])</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676e95;font-style:italic">  // ...</span></span>
<span class="line"><span style="color:#89ddff">}</span></span></code></pre><p>A common solution is to use <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify" rel="nofollow"><code>JSON.stringify</code></a>:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> map </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> new</span><span style="color:#82aaff"> Map</span><span style="color:#babed8">()</span></span>
<span class="line"><span style="color:#babed8">map</span><span style="color:#89ddff">.</span><span style="color:#82aaff">set</span><span style="color:#babed8">(JSON</span><span style="color:#89ddff">.</span><span style="color:#82aaff">stringify</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#babed8">])</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 3</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(map</span><span style="color:#89ddff">.</span><span style="color:#82aaff">get</span><span style="color:#babed8">(JSON</span><span style="color:#89ddff">.</span><span style="color:#82aaff">stringify</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#babed8">])))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; 3</span></span>
<span class="line"></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> set </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> new</span><span style="color:#82aaff"> Set</span><span style="color:#babed8">()</span></span>
<span class="line"><span style="color:#babed8">set</span><span style="color:#89ddff">.</span><span style="color:#82aaff">add</span><span style="color:#babed8">(JSON</span><span style="color:#89ddff">.</span><span style="color:#82aaff">stringify</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#babed8">]))</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(set</span><span style="color:#89ddff">.</span><span style="color:#82aaff">has</span><span style="color:#babed8">(JSON</span><span style="color:#89ddff">.</span><span style="color:#82aaff">stringify</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#babed8">])))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span></code></pre><p>This works because strings are primitives, which are compared by value rather than by reference. Unfortunately, using <code>JSON.stringify</code> requires that the inner values are stringifiable. If they’re not, then you’re forced to write a custom serialization function.</p><p>Plus, sometimes you <em>do</em> want reference equality, per inner value, but serialization doesn’t preserve reference equality:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> person1 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {</span><span style="color:#f07178"> name</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Tomer</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span><span style="color:#f07178"> occupation</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Software Engineer</span><span style="color:#89ddff">`</span><span style="color:#89ddff"> }</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> person2 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {</span><span style="color:#f07178"> name</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Tomer</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span><span style="color:#f07178"> occupation</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Software Engineer</span><span style="color:#89ddff">`</span><span style="color:#89ddff"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> salaryApplications </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> new</span><span style="color:#82aaff"> Map</span><span style="color:#babed8">()</span></span>
<span class="line"><span style="color:#babed8">salaryApplications</span><span style="color:#89ddff">.</span><span style="color:#82aaff">set</span><span style="color:#babed8">(JSON</span><span style="color:#89ddff">.</span><span style="color:#82aaff">stringify</span><span style="color:#babed8">([person1</span><span style="color:#89ddff">,</span><span style="color:#babed8"> Number</span><span style="color:#89ddff">.</span><span style="color:#babed8">MAX_VALUE])</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">approved</span><span style="color:#89ddff">`</span><span style="color:#babed8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676e95;font-style:italic">// Oh no! Two different software engineers named Tomer are considered the same due to stringifying!</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(salaryApplications</span><span style="color:#89ddff">.</span><span style="color:#82aaff">get</span><span style="color:#babed8">(JSON</span><span style="color:#89ddff">.</span><span style="color:#82aaff">stringify</span><span style="color:#babed8">([person2</span><span style="color:#89ddff">,</span><span style="color:#babed8"> Number</span><span style="color:#89ddff">.</span><span style="color:#babed8">MAX_VALUE])))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; approved</span></span></code></pre><p>Surely there’s a better way!</p><h2 id="the-solution-keyalesce" class="group relative"><a data-discover="true" href="#the-solution-keyalesce" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="The solution: &lt;code&gt;keyalesce&lt;/code&gt; permalink"/></a>The solution: <code>keyalesce</code></h2><p><a href="https://github.com/TomerAberbach/keyalesce" rel="nofollow"><code>keyalesce</code></a> is a module that returns the same unique key for the same value sequence<sup><a href="#fn-1" aria-describedby="footnote-label" data-footnote-ref="" id="fnref-1">1</a></sup>. It’s perfect for this use case:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> person1 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {</span><span style="color:#f07178"> name</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Tomer</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span><span style="color:#f07178"> occupation</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Software Engineer</span><span style="color:#89ddff">`</span><span style="color:#89ddff"> }</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> person2 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {</span><span style="color:#f07178"> name</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Tomer</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span><span style="color:#f07178"> occupation</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Software Engineer</span><span style="color:#89ddff">`</span><span style="color:#89ddff"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> map </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> new</span><span style="color:#82aaff"> Map</span><span style="color:#babed8">()</span></span>
<span class="line"><span style="color:#babed8">map</span><span style="color:#89ddff">.</span><span style="color:#82aaff">set</span><span style="color:#babed8">(</span><span style="color:#82aaff">keyalesce</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#babed8">])</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 3</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">map</span><span style="color:#89ddff">.</span><span style="color:#82aaff">set</span><span style="color:#babed8">(</span><span style="color:#82aaff">keyalesce</span><span style="color:#babed8">([</span><span style="color:#f78c6c">2</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">b</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 3</span><span style="color:#babed8">])</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 4</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">map</span><span style="color:#89ddff">.</span><span style="color:#82aaff">set</span><span style="color:#babed8">(</span><span style="color:#82aaff">keyalesce</span><span style="color:#babed8">([person1</span><span style="color:#89ddff">,</span><span style="color:#babed8"> Number</span><span style="color:#89ddff">.</span><span style="color:#babed8">MAX_VALUE])</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">approved</span><span style="color:#89ddff">`</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">map</span><span style="color:#89ddff">.</span><span style="color:#82aaff">set</span><span style="color:#babed8">(</span><span style="color:#82aaff">keyalesce</span><span style="color:#babed8">([person2</span><span style="color:#89ddff">,</span><span style="color:#babed8"> Number</span><span style="color:#89ddff">.</span><span style="color:#babed8">MAX_VALUE])</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">totally approved</span><span style="color:#89ddff">`</span><span style="color:#babed8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(map</span><span style="color:#89ddff">.</span><span style="color:#82aaff">get</span><span style="color:#babed8">(</span><span style="color:#82aaff">keyalesce</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#babed8">])))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; 3</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(map</span><span style="color:#89ddff">.</span><span style="color:#82aaff">get</span><span style="color:#babed8">(</span><span style="color:#82aaff">keyalesce</span><span style="color:#babed8">([</span><span style="color:#f78c6c">2</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">b</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 3</span><span style="color:#babed8">])))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; 4</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(map</span><span style="color:#89ddff">.</span><span style="color:#82aaff">get</span><span style="color:#babed8">(</span><span style="color:#82aaff">keyalesce</span><span style="color:#babed8">([person1</span><span style="color:#89ddff">,</span><span style="color:#babed8"> Number</span><span style="color:#89ddff">.</span><span style="color:#babed8">MAX_VALUE])))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; approved</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(map</span><span style="color:#89ddff">.</span><span style="color:#82aaff">get</span><span style="color:#babed8">(</span><span style="color:#82aaff">keyalesce</span><span style="color:#babed8">([person2</span><span style="color:#89ddff">,</span><span style="color:#babed8"> Number</span><span style="color:#89ddff">.</span><span style="color:#babed8">MAX_VALUE])))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; totally approved</span></span>
<span class="line"></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> set </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> new</span><span style="color:#82aaff"> Set</span><span style="color:#babed8">()</span></span>
<span class="line"><span style="color:#babed8">set</span><span style="color:#89ddff">.</span><span style="color:#82aaff">add</span><span style="color:#babed8">(</span><span style="color:#82aaff">keyalesce</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 3</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 4</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 5</span><span style="color:#babed8">]))</span></span>
<span class="line"><span style="color:#babed8">set</span><span style="color:#89ddff">.</span><span style="color:#82aaff">add</span><span style="color:#babed8">(</span><span style="color:#82aaff">keyalesce</span><span style="color:#babed8">([</span><span style="color:#f78c6c">3</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 3</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 1</span><span style="color:#babed8">]))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(set</span><span style="color:#89ddff">.</span><span style="color:#82aaff">has</span><span style="color:#babed8">(</span><span style="color:#82aaff">keyalesce</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 3</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 4</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 5</span><span style="color:#babed8">])))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(set</span><span style="color:#89ddff">.</span><span style="color:#82aaff">has</span><span style="color:#babed8">(</span><span style="color:#82aaff">keyalesce</span><span style="color:#babed8">([</span><span style="color:#f78c6c">3</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 3</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 1</span><span style="color:#babed8">])))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#82aaff">keyalesce</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 3</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 4</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 5</span><span style="color:#babed8">]) </span><span style="color:#89ddff">===</span><span style="color:#82aaff"> keyalesce</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 3</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 4</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 5</span><span style="color:#babed8">]))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span></code></pre><h2 id="how-does-it-work" class="group relative"><a data-discover="true" href="#how-does-it-work" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="How does it work? permalink"/></a>How does it work?</h2><p><code>keyalesce</code> internally maintains a <a href="https://en.wikipedia.org/wiki/Trie" rel="nofollow">trie</a> containing the sequences of values it has been called with. It creates and returns new keys for new sequences and returns previously created keys for known sequences!</p><p>For example, the following code:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> key1 </span><span style="color:#89ddff">=</span><span style="color:#82aaff"> keyalesce</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 3</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 4</span><span style="color:#babed8">])</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> key2 </span><span style="color:#89ddff">=</span><span style="color:#82aaff"> keyalesce</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 3</span><span style="color:#babed8">])</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> key3 </span><span style="color:#89ddff">=</span><span style="color:#82aaff"> keyalesce</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 7</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 8</span><span style="color:#babed8">])</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> key4 </span><span style="color:#89ddff">=</span><span style="color:#82aaff"> keyalesce</span><span style="color:#babed8">([</span><span style="color:#89ddff">`</span><span style="color:#c3e88d">a</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">b</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">c</span><span style="color:#89ddff">`</span><span style="color:#babed8">])</span></span></code></pre><p>Would result in the following trie:</p><svg aria-roledescription="flowchart-v2" class="flowchart" id="mermaid-0" style="font-family:Kantumruy Pro;font-size:16px;fill:#333;max-width:481.109375px" viewBox="0 0 481.109 335.219" width="100%" xmlns="http://www.w3.org/2000/svg"><style>@keyframes edge-animation-frame{0%{stroke-dashoffset:0}}@keyframes dash{to{stroke-dashoffset:0}}#mermaid-0 .edge-thickness-normal{stroke-width:1px}#mermaid-0 .edge-pattern-solid{stroke-dasharray:0}#mermaid-0 .marker{fill:#0b0b0b;stroke:#0b0b0b}#mermaid-0 .marker.cross{stroke:#0b0b0b}#mermaid-0 svg{font-family:Kantumruy Pro;font-size:16px}#mermaid-0 p{margin:0}#mermaid-0 .label{font-family:Kantumruy Pro;color:#333}#mermaid-0 span{fill:#333;color:#333}#mermaid-0 .node circle,#mermaid-0 .node path,#mermaid-0 .node rect{fill:#fff4dd;stroke:#edb;stroke-width:1px}#mermaid-0 .node .label{text-align:center}#mermaid-0 .flowchart-link{stroke:#0b0b0b;fill:none}#mermaid-0 .edgeLabel{background-color:#fdd;text-align:center}#mermaid-0 .edgeLabel p{background-color:#fdd}#mermaid-0 .edgeLabel rect{opacity:.5;background-color:#fdd;fill:#fdd}#mermaid-0 .labelBkg{background-color:rgba(244,221,255,.5)}#mermaid-0 :root{--mermaid-font-family:Kantumruy Pro}</style><marker class="flowchart-v2 marker" id="mermaid-0_flowchart-v2-pointEnd" markerHeight="8" markerUnits="userSpaceOnUse" markerWidth="8" orient="auto" refX="5" refY="5" viewBox="0 0 10 10"><path class="arrowMarkerPath" d="m0 0 10 5-10 5z" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker" id="mermaid-0_flowchart-v2-pointStart" markerHeight="8" markerUnits="userSpaceOnUse" markerWidth="8" orient="auto" refX="4.5" refY="5" viewBox="0 0 10 10"><path class="arrowMarkerPath" d="m0 5 10 5V0z" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker" id="mermaid-0_flowchart-v2-circleEnd" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="11" refY="5" viewBox="0 0 10 10"><circle class="arrowMarkerPath" r="5" cx="5" cy="5" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker class="flowchart-v2 marker" id="mermaid-0_flowchart-v2-circleStart" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="-1" refY="5" viewBox="0 0 10 10"><circle class="arrowMarkerPath" r="5" cx="5" cy="5" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker class="flowchart-v2 marker cross" id="mermaid-0_flowchart-v2-crossEnd" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="12" refY="5.2" viewBox="0 0 11 11"><path class="arrowMarkerPath" d="m1 1 9 9m0-9-9 9" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker cross" id="mermaid-0_flowchart-v2-crossStart" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="-1" refY="5.2" viewBox="0 0 11 11"><path class="arrowMarkerPath" d="m1 1 9 9m0-9-9 9" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><g class="root"><g class="edgePaths"><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="m47.543 152.217 5.152-4.684c5.151-4.683 15.455-14.049 24.321-18.733 8.867-4.683 16.297-4.683 20.011-4.683h3.715" data-edge="true" data-et="edge" data-id="L_R_1_0" data-points="W3sieCI6NDcuNTQzMDQzNjYwNTA0ODUsInkiOjE1Mi4yMTY1NzE0Nzc1NTU0OH0seyJ4Ijo3OC40NTMxMjUsInkiOjEyNC4xMTcxODc1fSx7IngiOjEwNC43NDIxODc1LCJ5IjoxMjQuMTE3MTg3NX1d" id="L_R_1_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="m38.293 188.934 6.693 18.958c6.694 18.957 20.08 56.872 30.274 75.83 10.193 18.958 17.193 18.958 20.693 18.958h3.5" data-edge="true" data-et="edge" data-id="L_R_a_0" data-points="W3sieCI6MzguMjkyODgxNTE4OTE5NzEsInkiOjE4OC45MzM5NjU5NjkyNDM0NX0seyJ4Ijo3OC40NTMxMjUsInkiOjMwMi42Nzk2ODc1fSx7IngiOjEwMy40NTMxMjUsInkiOjMwMi42Nzk2ODc1fV0=" id="L_R_a_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M125.555 124.117h47.648" data-edge="true" data-et="edge" data-id="L_1_2_0" data-points="W3sieCI6MTI1LjU1NDY4NzUsInkiOjEyNC4xMTcxODc1fSx7IngiOjE1MS44NDM3NSwieSI6MTI0LjExNzE4NzV9LHsieCI6MTc3LjIwMzEyNSwieSI6MTI0LjExNzE4NzV9XQ==" id="L_1_2_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="m196.482 114.625 5.006-6.507c5.006-6.506 15.017-19.519 23.522-26.026 8.506-6.506 15.506-6.506 19.006-6.506h3.5" data-edge="true" data-et="edge" data-id="L_2_3_0" data-points="W3sieCI6MTk2LjQ4MjQ0MzQ2MTkwMTgsInkiOjExNC42MjQ2NzQ0MTQ1NTY2MX0seyJ4IjoyMjYuNTE1NjI1LCJ5Ijo3NS41ODU5Mzc1fSx7IngiOjI1MS41MTU2MjUsInkiOjc1LjU4NTkzNzV9XQ==" id="L_2_3_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="m193.692 135.211 5.471 13.449c5.47 13.449 16.411 40.348 25.411 53.797s16.056 13.449 19.585 13.449h3.529" data-edge="true" data-et="edge" data-id="L_2_7_0" data-points="W3sieCI6MTkzLjY5MjIyNzg2NDc4MywieSI6MTM1LjIxMTEwNjA0OTAzNDQ0fSx7IngiOjIyNi41MTU2MjUsInkiOjIxNS45MDYyNX0seyJ4IjoyNTEuNjg3NSwieSI6MjE1LjkwNjI1fV0=" id="L_2_7_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="m271.233 66.497 4.864-5.694c4.865-5.695 14.595-17.085 22.995-22.78s15.471-5.695 19.006-5.695h3.535" data-edge="true" data-et="edge" data-id="L_3_K2_0" data-points="W3sieCI6MjcxLjIzMjU5MDI3NTI1NTA2LCJ5Ijo2Ni40OTc0ODQzMDY3NDY5fSx7IngiOjMwMC40MjE4NzUsInkiOjMyLjMyODEyNX0seyJ4IjozMjUuNjMyODEyNSwieSI6MzIuMzI4MTI1fV0=" id="L_3_K2_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="m271.233 84.674 4.864 5.695c4.865 5.695 14.595 17.085 25.019 22.78s21.54 5.695 27.099 5.695h5.558" data-edge="true" data-et="edge" data-id="L_3_4_0" data-points="W3sieCI6MjcxLjIzMjU5MDI3NTI1NTA2LCJ5Ijo4NC42NzQzOTA2OTMyNTMxfSx7IngiOjMwMC40MjE4NzUsInkiOjExOC44NDM3NX0seyJ4IjozMzcuNzczNDM3NSwieSI6MTE4Ljg0Mzc1fV0=" id="L_3_4_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M362.148 118.844h59.899" data-edge="true" data-et="edge" data-id="L_4_K1_0" data-points="W3sieCI6MzYyLjE0ODQzNzUsInkiOjExOC44NDM3NX0seyJ4IjozOTkuNSwieSI6MTE4Ljg0Mzc1fSx7IngiOjQyNi4wNDY4NzUsInkiOjExOC44NDM3NX1d" id="L_4_K1_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M275.25 215.906h58.477" data-edge="true" data-et="edge" data-id="L_7_8_0" data-points="W3sieCI6Mjc1LjI1LCJ5IjoyMTUuOTA2MjV9LHsieCI6MzAwLjQyMTg3NSwieSI6MjE1LjkwNjI1fSx7IngiOjMzNy43MjY1NjI1LCJ5IjoyMTUuOTA2MjV9XQ==" id="L_7_8_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M362.195 215.906H420.5" data-edge="true" data-et="edge" data-id="L_8_K3_0" data-points="W3sieCI6MzYyLjE5NTMxMjUsInkiOjIxNS45MDYyNX0seyJ4IjozOTkuNSwieSI6MjE1LjkwNjI1fSx7IngiOjQyNC41LCJ5IjoyMTUuOTA2MjV9XQ==" id="L_8_K3_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M126.844 302.68h46" data-edge="true" data-et="edge" data-id="L_a_b_0" data-points="W3sieCI6MTI2Ljg0Mzc1LCJ5IjozMDIuNjc5Njg3NX0seyJ4IjoxNTEuODQzNzUsInkiOjMwMi42Nzk2ODc1fSx7IngiOjE3Ni44NDM3NSwieSI6MzAyLjY3OTY4NzV9XQ==" id="L_a_b_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M201.516 302.68h46.148" data-edge="true" data-et="edge" data-id="L_b_c_0" data-points="W3sieCI6MjAxLjUxNTYyNSwieSI6MzAyLjY3OTY4NzV9LHsieCI6MjI2LjUxNTYyNSwieSI6MzAyLjY3OTY4NzV9LHsieCI6MjUxLjY2NDA2MjUsInkiOjMwMi42Nzk2ODc1fV0=" id="L_b_c_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M275.273 302.68h46.149" data-edge="true" data-et="edge" data-id="L_c_K4_0" data-points="W3sieCI6Mjc1LjI3MzQzNzUsInkiOjMwMi42Nzk2ODc1fSx7IngiOjMwMC40MjE4NzUsInkiOjMwMi42Nzk2ODc1fSx7IngiOjMyNS40MjE4NzUsInkiOjMwMi42Nzk2ODc1fV0=" id="L_c_K4_0" marker-end="url(#mermaid-0_flowchart-v2-pointEnd)"></path></g><g class="edgeLabels"><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_R_1_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_R_a_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_1_2_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_2_3_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_2_7_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_3_K2_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_3_4_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_4_K1_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_7_8_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_8_K3_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_a_b_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_b_c_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_c_K4_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="nodes"><g class="default node" transform="translate(115.148 124.117)" id="flowchart-1-5"><circle class="basic label-container" r="10.406"></circle><g class="label" transform="translate(-2.906 -12)"><rect></rect><foreignObject height="24" width="5.813"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>1</p></span></div></foreignObject></g></g><g class="default node" transform="translate(189.18 124.117)" id="flowchart-2-6"><circle class="basic label-container" r="11.977"></circle><g class="label" transform="translate(-4.477 -12)"><rect></rect><foreignObject height="24" width="8.953"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>2</p></span></div></foreignObject></g></g><g class="default node" transform="translate(263.469 75.586)" id="flowchart-3-7"><circle class="basic label-container" r="11.953"></circle><g class="label" transform="translate(-4.453 -12)"><rect></rect><foreignObject height="24" width="8.906"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>3</p></span></div></foreignObject></g></g><g class="default node" transform="translate(349.96 118.844)" id="flowchart-4-8"><circle class="basic label-container" r="12.188"></circle><g class="label" transform="translate(-4.688 -12)"><rect></rect><foreignObject height="24" width="9.375"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>4</p></span></div></foreignObject></g></g><g class="default node" transform="translate(263.469 215.906)" id="flowchart-7-9"><circle class="basic label-container" r="11.781"></circle><g class="label" transform="translate(-4.281 -12)"><rect></rect><foreignObject height="24" width="8.563"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>7</p></span></div></foreignObject></g></g><g class="default node" transform="translate(349.96 215.906)" id="flowchart-8-10"><circle class="basic label-container" r="12.234"></circle><g class="label" transform="translate(-4.734 -12)"><rect></rect><foreignObject height="24" width="9.469"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>8</p></span></div></foreignObject></g></g><g class="default node" transform="translate(30.727 167.504)" id="flowchart-R-0"><circle class="basic label-container" r="22.727"></circle><g class="label" transform="translate(-15.227 -12)"><rect></rect><foreignObject height="24" width="30.453"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>root</p></span></div></foreignObject></g></g><g class="default node" transform="translate(448.805 118.844)" id="flowchart-K1-1"><circle class="basic label-container" r="22.758"></circle><g class="label" transform="translate(-15.258 -12)"><rect></rect><foreignObject height="24" width="30.516"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>key1</p></span></div></foreignObject></g></g><g class="default node" transform="translate(349.96 32.328)" id="flowchart-K2-2"><circle class="basic label-container" r="24.328"></circle><g class="label" transform="translate(-16.828 -12)"><rect></rect><foreignObject height="24" width="33.656"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>key2</p></span></div></foreignObject></g></g><g class="default node" transform="translate(448.805 215.906)" id="flowchart-K3-3"><circle class="basic label-container" r="24.305"></circle><g class="label" transform="translate(-16.805 -12)"><rect></rect><foreignObject height="24" width="33.609"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>key3</p></span></div></foreignObject></g></g><g class="default node" transform="translate(349.96 302.68)" id="flowchart-K4-4"><circle class="basic label-container" r="24.539"></circle><g class="label" transform="translate(-17.04 -12)"><rect></rect><foreignObject height="24" width="34.078"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>key4</p></span></div></foreignObject></g></g><g class="default node" transform="translate(115.148 302.68)" id="flowchart-a-11"><circle class="basic label-container" r="11.695"></circle><g class="label" transform="translate(-4.195 -12)"><rect></rect><foreignObject height="24" width="8.391"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>a</p></span></div></foreignObject></g></g><g class="default node" transform="translate(189.18 302.68)" id="flowchart-b-12"><circle class="basic label-container" r="12.336"></circle><g class="label" transform="translate(-4.836 -12)"><rect></rect><foreignObject height="24" width="9.672"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>b</p></span></div></foreignObject></g></g><g class="default node" transform="translate(263.469 302.68)" id="flowchart-c-13"><circle class="basic label-container" r="11.805"></circle><g class="label" transform="translate(-4.305 -12)"><rect></rect><foreignObject height="24" width="8.609"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>c</p></span></div></foreignObject></g></g></g></g></svg><p>And calling <code>keyalesce([1, 2, 3, 4])</code> again would return <code>key1</code> after traversing nodes <code>1</code>, <code>2</code>, <code>3</code>, and <code>4</code> in the trie.</p><h3 id="does-keyalesce-cause-memory-leaks" class="group relative"><a data-discover="true" href="#does-keyalesce-cause-memory-leaks" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Does &lt;code&gt;keyalesce&lt;/code&gt; cause memory leaks? permalink"/></a>Does <code>keyalesce</code> cause memory leaks?</h3><p>A long running program using a naive implementation of <code>keyalesce</code> would have a <a href="https://en.wikipedia.org/wiki/Memory_leak" rel="nofollow">memory leak</a> due to unbounded growth of the trie. How are <a href="https://en.wikipedia.org/wiki/Unreachable_memory" rel="nofollow">unreachable</a> nodes pruned?</p><h4 id="sequence-value-reachability" class="group relative"><a data-discover="true" href="#sequence-value-reachability" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Sequence value reachability permalink"/></a>Sequence value reachability</h4><p>Consider the following code:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">let</span><span style="color:#babed8"> object1 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {}</span></span>
<span class="line"><span style="color:#c792ea">let</span><span style="color:#babed8"> object2 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> key1 </span><span style="color:#89ddff">=</span><span style="color:#82aaff"> keyalesce</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#babed8"> object1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 4</span><span style="color:#babed8">])</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> key2 </span><span style="color:#89ddff">=</span><span style="color:#82aaff"> keyalesce</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#89ddff">,</span><span style="color:#babed8"> object2</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 3</span><span style="color:#babed8">])</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676e95;font-style:italic">// ...</span></span></code></pre><p>Which would result in the following trie:</p><svg aria-roledescription="flowchart-v2" class="flowchart" id="mermaid-1" style="font-family:Kantumruy Pro;font-size:16px;fill:#333;max-width:564.078125px" viewBox="0 0 564.078 182.203" width="100%" xmlns="http://www.w3.org/2000/svg"><style>@keyframes edge-animation-frame{0%{stroke-dashoffset:0}}@keyframes dash{to{stroke-dashoffset:0}}#mermaid-1 .edge-thickness-normal{stroke-width:1px}#mermaid-1 .edge-pattern-solid{stroke-dasharray:0}#mermaid-1 .marker{fill:#0b0b0b;stroke:#0b0b0b}#mermaid-1 .marker.cross{stroke:#0b0b0b}#mermaid-1 svg{font-family:Kantumruy Pro;font-size:16px}#mermaid-1 p{margin:0}#mermaid-1 .label{font-family:Kantumruy Pro;color:#333}#mermaid-1 span{fill:#333;color:#333}#mermaid-1 .node circle,#mermaid-1 .node path,#mermaid-1 .node rect{fill:#fff4dd;stroke:#edb;stroke-width:1px}#mermaid-1 .node .label{text-align:center}#mermaid-1 .flowchart-link{stroke:#0b0b0b;fill:none}#mermaid-1 .edgeLabel{background-color:#fdd;text-align:center}#mermaid-1 .edgeLabel p{background-color:#fdd}#mermaid-1 .edgeLabel rect{opacity:.5;background-color:#fdd;fill:#fdd}#mermaid-1 .labelBkg{background-color:rgba(244,221,255,.5)}#mermaid-1 :root{--mermaid-font-family:Kantumruy Pro}</style><marker class="flowchart-v2 marker" id="mermaid-1_flowchart-v2-pointEnd" markerHeight="8" markerUnits="userSpaceOnUse" markerWidth="8" orient="auto" refX="5" refY="5" viewBox="0 0 10 10"><path class="arrowMarkerPath" d="m0 0 10 5-10 5z" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker" id="mermaid-1_flowchart-v2-pointStart" markerHeight="8" markerUnits="userSpaceOnUse" markerWidth="8" orient="auto" refX="4.5" refY="5" viewBox="0 0 10 10"><path class="arrowMarkerPath" d="m0 5 10 5V0z" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker" id="mermaid-1_flowchart-v2-circleEnd" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="11" refY="5" viewBox="0 0 10 10"><circle class="arrowMarkerPath" r="5" cx="5" cy="5" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker class="flowchart-v2 marker" id="mermaid-1_flowchart-v2-circleStart" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="-1" refY="5" viewBox="0 0 10 10"><circle class="arrowMarkerPath" r="5" cx="5" cy="5" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker class="flowchart-v2 marker cross" id="mermaid-1_flowchart-v2-crossEnd" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="12" refY="5.2" viewBox="0 0 11 11"><path class="arrowMarkerPath" d="m1 1 9 9m0-9-9 9" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker cross" id="mermaid-1_flowchart-v2-crossStart" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="-1" refY="5.2" viewBox="0 0 11 11"><path class="arrowMarkerPath" d="m1 1 9 9m0-9-9 9" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><g class="root"><g class="edgePaths"><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M53.453 91.887h46" data-edge="true" data-et="edge" data-id="L_R_1_0" data-points="W3sieCI6NTMuNDUzMTI1LCJ5Ijo5MS44ODY3MTg3NX0seyJ4Ijo3OC40NTMxMjUsInkiOjkxLjg4NjcxODc1fSx7IngiOjEwMy40NTMxMjUsInkiOjkxLjg4NjcxODc1fV0=" id="L_R_1_0" marker-end="url(#mermaid-1_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="m119.979 100.303 4.881 6.713c4.881 6.712 14.644 20.137 23.025 26.85 8.381 6.712 15.381 6.712 18.881 6.712h3.5" data-edge="true" data-et="edge" data-id="L_1_O1_0" data-points="W3sieCI6MTE5Ljk3OTM5NDM1Nzk4MTczLCJ5IjoxMDAuMzAzMDk1NzY4NjcxOTJ9LHsieCI6MTQ5LjI2NTYyNSwieSI6MTQwLjU3ODEyNX0seyJ4IjoxNzQuMjY1NjI1LCJ5IjoxNDAuNTc4MTI1fV0=" id="L_1_O1_0" marker-end="url(#mermaid-1_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="m119.979 83.47 4.881-6.712c4.881-6.713 14.644-20.138 26.633-26.85 11.989-6.713 26.205-6.713 33.313-6.713h7.108" data-edge="true" data-et="edge" data-id="L_1_2_0" data-points="W3sieCI6MTE5Ljk3OTM5NDM1Nzk4MTczLCJ5Ijo4My40NzAzNDE3MzEzMjgwOH0seyJ4IjoxNDkuMjY1NjI1LCJ5Ijo0My4xOTUzMTI1fSx7IngiOjE5NS45MTQwNjI1LCJ5Ijo0My4xOTUzMTI1fV0=" id="L_1_2_0" marker-end="url(#mermaid-1_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M241.516 140.578h69.007" data-edge="true" data-et="edge" data-id="L_O1_4_0" data-points="W3sieCI6MjQxLjUxNTYyNSwieSI6MTQwLjU3ODEyNX0seyJ4IjoyNjYuNTE1NjI1LCJ5IjoxNDAuNTc4MTI1fSx7IngiOjMxNC41MjM0Mzc1LCJ5IjoxNDAuNTc4MTI1fV0=" id="L_O1_4_0" marker-end="url(#mermaid-1_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M338.898 140.578h69.008" data-edge="true" data-et="edge" data-id="L_4_K1_0" data-points="W3sieCI6MzM4Ljg5ODQzNzUsInkiOjE0MC41NzgxMjV9LHsieCI6Mzg2LjkwNjI1LCJ5IjoxNDAuNTc4MTI1fSx7IngiOjQxMS45MDYyNSwieSI6MTQwLjU3ODEyNX1d" id="L_4_K1_0" marker-end="url(#mermaid-1_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M219.867 43.195h67.649" data-edge="true" data-et="edge" data-id="L_2_O2_0" data-points="W3sieCI6MjE5Ljg2NzE4NzUsInkiOjQzLjE5NTMxMjV9LHsieCI6MjY2LjUxNTYyNSwieSI6NDMuMTk1MzEyNX0seyJ4IjoyOTEuNTE1NjI1LCJ5Ijo0My4xOTUzMTI1fV0=" id="L_2_O2_0" marker-end="url(#mermaid-1_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M361.906 43.195h56.805" data-edge="true" data-et="edge" data-id="L_O2_3_0" data-points="W3sieCI6MzYxLjkwNjI1LCJ5Ijo0My4xOTUzMTI1fSx7IngiOjM4Ni45MDYyNSwieSI6NDMuMTk1MzEyNX0seyJ4Ijo0MjIuNzEwOTM3NSwieSI6NDMuMTk1MzEyNX1d" id="L_O2_3_0" marker-end="url(#mermaid-1_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M446.617 43.195h56.805" data-edge="true" data-et="edge" data-id="L_3_K2_0" data-points="W3sieCI6NDQ2LjYxNzE4NzUsInkiOjQzLjE5NTMxMjV9LHsieCI6NDgyLjQyMTg3NSwieSI6NDMuMTk1MzEyNX0seyJ4Ijo1MDcuNDIxODc1LCJ5Ijo0My4xOTUzMTI1fV0=" id="L_3_K2_0" marker-end="url(#mermaid-1_flowchart-v2-pointEnd)"></path></g><g class="edgeLabels"><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_R_1_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_1_O1_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_1_2_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_O1_4_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_4_K1_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_2_O2_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_O2_3_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_3_K2_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="nodes"><g class="default node" transform="translate(113.86 91.887)" id="flowchart-1-3"><circle class="basic label-container" r="10.406"></circle><g class="label" transform="translate(-2.906 -12)"><rect></rect><foreignObject height="24" width="5.813"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>1</p></span></div></foreignObject></g></g><g class="default node" transform="translate(207.89 43.195)" id="flowchart-2-4"><circle class="basic label-container" r="11.977"></circle><g class="label" transform="translate(-4.477 -12)"><rect></rect><foreignObject height="24" width="8.953"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>2</p></span></div></foreignObject></g></g><g class="default node" transform="translate(434.664 43.195)" id="flowchart-3-5"><circle class="basic label-container" r="11.953"></circle><g class="label" transform="translate(-4.453 -12)"><rect></rect><foreignObject height="24" width="8.906"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>3</p></span></div></foreignObject></g></g><g class="default node" transform="translate(326.71 140.578)" id="flowchart-4-6"><circle class="basic label-container" r="12.188"></circle><g class="label" transform="translate(-4.688 -12)"><rect></rect><foreignObject height="24" width="9.375"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>4</p></span></div></foreignObject></g></g><g class="default node" transform="translate(30.727 91.887)" id="flowchart-R-0"><circle class="basic label-container" r="22.727"></circle><g class="label" transform="translate(-15.227 -12)"><rect></rect><foreignObject height="24" width="30.453"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>root</p></span></div></foreignObject></g></g><g class="default node" transform="translate(434.664 140.578)" id="flowchart-K1-1"><circle class="basic label-container" r="22.758"></circle><g class="label" transform="translate(-15.258 -12)"><rect></rect><foreignObject height="24" width="30.516"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>key1</p></span></div></foreignObject></g></g><g class="default node" transform="translate(531.75 43.195)" id="flowchart-K2-2"><circle class="basic label-container" r="24.328"></circle><g class="label" transform="translate(-16.828 -12)"><rect></rect><foreignObject height="24" width="33.656"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>key2</p></span></div></foreignObject></g></g><g class="default node" transform="translate(207.89 140.578)" id="flowchart-O1-7"><circle class="basic label-container" r="33.625"></circle><g class="label" transform="translate(-26.125 -12)"><rect></rect><foreignObject height="24" width="52.25"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>object1</p></span></div></foreignObject></g></g><g class="default node" transform="translate(326.71 43.195)" id="flowchart-O2-8"><circle class="basic label-container" r="35.195"></circle><g class="label" transform="translate(-27.695 -12)"><rect></rect><foreignObject height="24" width="55.391"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>object2</p></span></div></foreignObject></g></g></g></g></svg><p>If the code continues like so:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#babed8">object2 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> null</span></span></code></pre><p>Then <code>object2</code>’s original value is only reachable from the trie. <code>keyalesce</code> can now prune the associated sequence and its key from the trie because it has become <em>impossible</em> for <code>keyalesce</code> to be called with that sequence ever again.</p><p>I made the trie hold only <a href="https://en.wikipedia.org/wiki/Weak_reference" rel="nofollow">weak references</a> to objects passed to <code>keyalesce</code> and pruned the trie when the objects are <a href="https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)" rel="nofollow">garbage-collected</a> using <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry" rel="nofollow"><code>FinalizationRegistry</code></a>.</p><p>After pruning in this case the trie would look like so:</p><svg aria-roledescription="flowchart-v2" class="flowchart" id="mermaid-2" style="font-family:Kantumruy Pro;font-size:16px;fill:#333;max-width:424.03125px" viewBox="0 0 424.031 83.25" width="100%" xmlns="http://www.w3.org/2000/svg"><style>@keyframes edge-animation-frame{0%{stroke-dashoffset:0}}@keyframes dash{to{stroke-dashoffset:0}}#mermaid-2 .edge-thickness-normal{stroke-width:1px}#mermaid-2 .edge-pattern-solid{stroke-dasharray:0}#mermaid-2 .marker{fill:#0b0b0b;stroke:#0b0b0b}#mermaid-2 .marker.cross{stroke:#0b0b0b}#mermaid-2 svg{font-family:Kantumruy Pro;font-size:16px}#mermaid-2 p{margin:0}#mermaid-2 .label{font-family:Kantumruy Pro;color:#333}#mermaid-2 span{fill:#333;color:#333}#mermaid-2 .node circle,#mermaid-2 .node path,#mermaid-2 .node rect{fill:#fff4dd;stroke:#edb;stroke-width:1px}#mermaid-2 .node .label{text-align:center}#mermaid-2 .flowchart-link{stroke:#0b0b0b;fill:none}#mermaid-2 .edgeLabel{background-color:#fdd;text-align:center}#mermaid-2 .edgeLabel p{background-color:#fdd}#mermaid-2 .edgeLabel rect{opacity:.5;background-color:#fdd;fill:#fdd}#mermaid-2 .labelBkg{background-color:rgba(244,221,255,.5)}#mermaid-2 :root{--mermaid-font-family:Kantumruy Pro}</style><marker class="flowchart-v2 marker" id="mermaid-2_flowchart-v2-pointEnd" markerHeight="8" markerUnits="userSpaceOnUse" markerWidth="8" orient="auto" refX="5" refY="5" viewBox="0 0 10 10"><path class="arrowMarkerPath" d="m0 0 10 5-10 5z" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker" id="mermaid-2_flowchart-v2-pointStart" markerHeight="8" markerUnits="userSpaceOnUse" markerWidth="8" orient="auto" refX="4.5" refY="5" viewBox="0 0 10 10"><path class="arrowMarkerPath" d="m0 5 10 5V0z" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker" id="mermaid-2_flowchart-v2-circleEnd" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="11" refY="5" viewBox="0 0 10 10"><circle class="arrowMarkerPath" r="5" cx="5" cy="5" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker class="flowchart-v2 marker" id="mermaid-2_flowchart-v2-circleStart" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="-1" refY="5" viewBox="0 0 10 10"><circle class="arrowMarkerPath" r="5" cx="5" cy="5" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker class="flowchart-v2 marker cross" id="mermaid-2_flowchart-v2-crossEnd" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="12" refY="5.2" viewBox="0 0 11 11"><path class="arrowMarkerPath" d="m1 1 9 9m0-9-9 9" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker cross" id="mermaid-2_flowchart-v2-crossStart" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="-1" refY="5.2" viewBox="0 0 11 11"><path class="arrowMarkerPath" d="m1 1 9 9m0-9-9 9" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><g class="root"><g class="edgePaths"><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M53.453 41.625h46" data-edge="true" data-et="edge" data-id="L_R_1_0" data-points="W3sieCI6NTMuNDUzMTI1LCJ5Ijo0MS42MjV9LHsieCI6NzguNDUzMTI1LCJ5Ijo0MS42MjV9LHsieCI6MTAzLjQ1MzEyNSwieSI6NDEuNjI1fV0=" id="L_R_1_0" marker-end="url(#mermaid-2_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M124.266 41.625h46" data-edge="true" data-et="edge" data-id="L_1_O1_0" data-points="W3sieCI6MTI0LjI2NTYyNSwieSI6NDEuNjI1fSx7IngiOjE0OS4yNjU2MjUsInkiOjQxLjYyNX0seyJ4IjoxNzQuMjY1NjI1LCJ5Ijo0MS42MjV9XQ==" id="L_1_O1_0" marker-end="url(#mermaid-2_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M241.516 41.625h46" data-edge="true" data-et="edge" data-id="L_O1_4_0" data-points="W3sieCI6MjQxLjUxNTYyNSwieSI6NDEuNjI1fSx7IngiOjI2Ni41MTU2MjUsInkiOjQxLjYyNX0seyJ4IjoyOTEuNTE1NjI1LCJ5Ijo0MS42MjV9XQ==" id="L_O1_4_0" marker-end="url(#mermaid-2_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M315.891 41.625h46" data-edge="true" data-et="edge" data-id="L_4_K1_0" data-points="W3sieCI6MzE1Ljg5MDYyNSwieSI6NDEuNjI1fSx7IngiOjM0MC44OTA2MjUsInkiOjQxLjYyNX0seyJ4IjozNjUuODkwNjI1LCJ5Ijo0MS42MjV9XQ==" id="L_4_K1_0" marker-end="url(#mermaid-2_flowchart-v2-pointEnd)"></path></g><g class="edgeLabels"><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_R_1_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_1_O1_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_O1_4_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_4_K1_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="nodes"><g class="default node" transform="translate(113.86 41.625)" id="flowchart-1-2"><circle class="basic label-container" r="10.406"></circle><g class="label" transform="translate(-2.906 -12)"><rect></rect><foreignObject height="24" width="5.813"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>1</p></span></div></foreignObject></g></g><g class="default node" transform="translate(303.703 41.625)" id="flowchart-4-3"><circle class="basic label-container" r="12.188"></circle><g class="label" transform="translate(-4.688 -12)"><rect></rect><foreignObject height="24" width="9.375"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>4</p></span></div></foreignObject></g></g><g class="default node" transform="translate(30.727 41.625)" id="flowchart-R-0"><circle class="basic label-container" r="22.727"></circle><g class="label" transform="translate(-15.227 -12)"><rect></rect><foreignObject height="24" width="30.453"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>root</p></span></div></foreignObject></g></g><g class="default node" transform="translate(390.96 41.625)" id="flowchart-K1-1"><circle class="basic label-container" r="25.07"></circle><g class="label" transform="translate(-17.57 -12)"><rect></rect><foreignObject height="24" width="35.141"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>key 1</p></span></div></foreignObject></g></g><g class="default node" transform="translate(207.89 41.625)" id="flowchart-O1-4"><circle class="basic label-container" r="33.625"></circle><g class="label" transform="translate(-26.125 -12)"><rect></rect><foreignObject height="24" width="52.25"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>object1</p></span></div></foreignObject></g></g></g></g></svg><div class="relative my-10 rounded-md border-l-4 p-8 border-l-blue-500 bg-blue-50" role="note"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;%23374151&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M11.25%2011.25l.041-.02a.75.75%200%20011.063.852l-.708%202.836a.75.75%200%20001.063.853l.041-.021M21%2012a9%209%200%2011-18%200%209%209%200%200118%200zm-9-3.75h.008v.008H12V8.25z&#x27;%20/%3e%3c/svg%3e" alt="" class="not-prose absolute top-0 left-0 m-0 box-content size-9 -translate-x-1/2 -translate-y-[45%] rounded-full border-4 border-blue-50 bg-blue-50"/><header class="pb-2 text-base font-semibold uppercase">Note</header><div class="*:first:mt-0 *:last:mb-0"><p>Although <code>1</code> was in the sequence it was not pruned because it is still needed for another sequence.</p></div></div><h4 id="created-key-reachability" class="group relative"><a data-discover="true" href="#created-key-reachability" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Created key reachability permalink"/></a>Created key reachability</h4><p>Consider the following code:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">let</span><span style="color:#babed8"> key1 </span><span style="color:#89ddff">=</span><span style="color:#82aaff"> keyalesce</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 4</span><span style="color:#babed8">])</span></span>
<span class="line"><span style="color:#c792ea">let</span><span style="color:#babed8"> key2 </span><span style="color:#89ddff">=</span><span style="color:#82aaff"> keyalesce</span><span style="color:#babed8">([</span><span style="color:#f78c6c">1</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 2</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 5</span><span style="color:#89ddff">,</span><span style="color:#f78c6c"> 7</span><span style="color:#babed8">])</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676e95;font-style:italic">// ...</span></span></code></pre><p>Which would result in the following trie:</p><svg aria-roledescription="flowchart-v2" class="flowchart" id="mermaid-3" style="font-family:Kantumruy Pro;font-size:16px;fill:#333;max-width:474.765625px" viewBox="0 0 474.766 147.625" width="100%" xmlns="http://www.w3.org/2000/svg"><style>@keyframes edge-animation-frame{0%{stroke-dashoffset:0}}@keyframes dash{to{stroke-dashoffset:0}}#mermaid-3 .edge-thickness-normal{stroke-width:1px}#mermaid-3 .edge-pattern-solid{stroke-dasharray:0}#mermaid-3 .marker{fill:#0b0b0b;stroke:#0b0b0b}#mermaid-3 .marker.cross{stroke:#0b0b0b}#mermaid-3 svg{font-family:Kantumruy Pro;font-size:16px}#mermaid-3 p{margin:0}#mermaid-3 .label{font-family:Kantumruy Pro;color:#333}#mermaid-3 span{fill:#333;color:#333}#mermaid-3 .node circle,#mermaid-3 .node path,#mermaid-3 .node rect{fill:#fff4dd;stroke:#edb;stroke-width:1px}#mermaid-3 .node .label{text-align:center}#mermaid-3 .flowchart-link{stroke:#0b0b0b;fill:none}#mermaid-3 .edgeLabel{background-color:#fdd;text-align:center}#mermaid-3 .edgeLabel p{background-color:#fdd}#mermaid-3 .edgeLabel rect{opacity:.5;background-color:#fdd;fill:#fdd}#mermaid-3 .labelBkg{background-color:rgba(244,221,255,.5)}#mermaid-3 :root{--mermaid-font-family:Kantumruy Pro}</style><marker class="flowchart-v2 marker" id="mermaid-3_flowchart-v2-pointEnd" markerHeight="8" markerUnits="userSpaceOnUse" markerWidth="8" orient="auto" refX="5" refY="5" viewBox="0 0 10 10"><path class="arrowMarkerPath" d="m0 0 10 5-10 5z" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker" id="mermaid-3_flowchart-v2-pointStart" markerHeight="8" markerUnits="userSpaceOnUse" markerWidth="8" orient="auto" refX="4.5" refY="5" viewBox="0 0 10 10"><path class="arrowMarkerPath" d="m0 5 10 5V0z" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker" id="mermaid-3_flowchart-v2-circleEnd" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="11" refY="5" viewBox="0 0 10 10"><circle class="arrowMarkerPath" r="5" cx="5" cy="5" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker class="flowchart-v2 marker" id="mermaid-3_flowchart-v2-circleStart" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="-1" refY="5" viewBox="0 0 10 10"><circle class="arrowMarkerPath" r="5" cx="5" cy="5" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker class="flowchart-v2 marker cross" id="mermaid-3_flowchart-v2-crossEnd" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="12" refY="5.2" viewBox="0 0 11 11"><path class="arrowMarkerPath" d="m1 1 9 9m0-9-9 9" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker cross" id="mermaid-3_flowchart-v2-crossStart" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="-1" refY="5.2" viewBox="0 0 11 11"><path class="arrowMarkerPath" d="m1 1 9 9m0-9-9 9" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><g class="root"><g class="edgePaths"><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M53.453 74.598h46" data-edge="true" data-et="edge" data-id="L_R_1_0" data-points="W3sieCI6NTMuNDUzMTI1LCJ5Ijo3NC41OTc2NTYyNX0seyJ4Ijo3OC40NTMxMjUsInkiOjc0LjU5NzY1NjI1fSx7IngiOjEwMy40NTMxMjUsInkiOjc0LjU5NzY1NjI1fV0=" id="L_R_1_0" marker-end="url(#mermaid-3_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M124.266 74.598h46" data-edge="true" data-et="edge" data-id="L_1_2_0" data-points="W3sieCI6MTI0LjI2NTYyNSwieSI6NzQuNTk3NjU2MjV9LHsieCI6MTQ5LjI2NTYyNSwieSI6NzQuNTk3NjU2MjV9LHsieCI6MTc0LjI2NTYyNSwieSI6NzQuNTk3NjU2MjV9XQ==" id="L_1_2_0" marker-end="url(#mermaid-3_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="m194.128 83.612 4.848 5.542c4.849 5.543 14.546 16.628 22.894 22.171 8.349 5.542 15.349 5.542 18.849 5.542h3.5" data-edge="true" data-et="edge" data-id="L_2_4_0" data-points="W3sieCI6MTk0LjEyNzY4ODQ5MDI1NTg4LCJ5Ijo4My42MTE5MTg0NzQzMzUzOH0seyJ4IjoyMjMuMjE4NzUsInkiOjExNi44NjcxODc1fSx7IngiOjI0OC4yMTg3NSwieSI6MTE2Ljg2NzE4NzV9XQ==" id="L_2_4_0" marker-end="url(#mermaid-3_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="m194.128 65.583 4.848-5.542c4.849-5.543 14.546-16.628 22.931-22.17s15.458-5.543 18.994-5.543h3.537" data-edge="true" data-et="edge" data-id="L_2_5_0" data-points="W3sieCI6MTk0LjEyNzY4ODQ5MDI1NTg4LCJ5Ijo2NS41ODMzOTQwMjU2NjQ2Mn0seyJ4IjoyMjMuMjE4NzUsInkiOjMyLjMyODEyNX0seyJ4IjoyNDguNDM3NSwieSI6MzIuMzI4MTI1fV0=" id="L_2_5_0" marker-end="url(#mermaid-3_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M272.594 116.867h46" data-edge="true" data-et="edge" data-id="L_4_K1_0" data-points="W3sieCI6MjcyLjU5Mzc1LCJ5IjoxMTYuODY3MTg3NX0seyJ4IjoyOTcuNTkzNzUsInkiOjExNi44NjcxODc1fSx7IngiOjMyMi41OTM3NSwieSI6MTE2Ljg2NzE4NzV9XQ==" id="L_4_K1_0" marker-end="url(#mermaid-3_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M272.375 32.328h57.195" data-edge="true" data-et="edge" data-id="L_5_7_0" data-points="W3sieCI6MjcyLjM3NSwieSI6MzIuMzI4MTI1fSx7IngiOjI5Ny41OTM3NSwieSI6MzIuMzI4MTI1fSx7IngiOjMzMy41NzAzMTI1LCJ5IjozMi4zMjgxMjV9XQ==" id="L_5_7_0" marker-end="url(#mermaid-3_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M357.133 32.328h56.976" data-edge="true" data-et="edge" data-id="L_7_K2_0" data-points="W3sieCI6MzU3LjEzMjgxMjUsInkiOjMyLjMyODEyNX0seyJ4IjozOTMuMTA5Mzc1LCJ5IjozMi4zMjgxMjV9LHsieCI6NDE4LjEwOTM3NSwieSI6MzIuMzI4MTI1fV0=" id="L_7_K2_0" marker-end="url(#mermaid-3_flowchart-v2-pointEnd)"></path></g><g class="edgeLabels"><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_R_1_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_1_2_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_2_4_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_2_5_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_4_K1_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_5_7_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_7_K2_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="nodes"><g class="default node" transform="translate(113.86 74.598)" id="flowchart-1-3"><circle class="basic label-container" r="10.406"></circle><g class="label" transform="translate(-2.906 -12)"><rect></rect><foreignObject height="24" width="5.813"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>1</p></span></div></foreignObject></g></g><g class="default node" transform="translate(186.242 74.598)" id="flowchart-2-4"><circle class="basic label-container" r="11.977"></circle><g class="label" transform="translate(-4.477 -12)"><rect></rect><foreignObject height="24" width="8.953"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>2</p></span></div></foreignObject></g></g><g class="default node" transform="translate(260.406 116.867)" id="flowchart-4-5"><circle class="basic label-container" r="12.188"></circle><g class="label" transform="translate(-4.688 -12)"><rect></rect><foreignObject height="24" width="9.375"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>4</p></span></div></foreignObject></g></g><g class="default node" transform="translate(260.406 32.328)" id="flowchart-5-6"><circle class="basic label-container" r="11.969"></circle><g class="label" transform="translate(-4.469 -12)"><rect></rect><foreignObject height="24" width="8.938"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>5</p></span></div></foreignObject></g></g><g class="default node" transform="translate(345.352 32.328)" id="flowchart-7-7"><circle class="basic label-container" r="11.781"></circle><g class="label" transform="translate(-4.281 -12)"><rect></rect><foreignObject height="24" width="8.563"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>7</p></span></div></foreignObject></g></g><g class="default node" transform="translate(30.727 74.598)" id="flowchart-R-0"><circle class="basic label-container" r="22.727"></circle><g class="label" transform="translate(-15.227 -12)"><rect></rect><foreignObject height="24" width="30.453"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>root</p></span></div></foreignObject></g></g><g class="default node" transform="translate(345.352 116.867)" id="flowchart-K1-1"><circle class="basic label-container" r="22.758"></circle><g class="label" transform="translate(-15.258 -12)"><rect></rect><foreignObject height="24" width="30.516"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>key1</p></span></div></foreignObject></g></g><g class="default node" transform="translate(442.438 32.328)" id="flowchart-K2-2"><circle class="basic label-container" r="24.328"></circle><g class="label" transform="translate(-16.828 -12)"><rect></rect><foreignObject height="24" width="33.656"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>key2</p></span></div></foreignObject></g></g></g></g></svg><p>The previous section’s logic does not apply because all of the sequence values are primitives, which are always reachable and not eligible for garbage collection. So how is the trie pruned in this case?</p><p>If the code continues like so:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#babed8">key2 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> null</span></span></code></pre><p>Then <code>key2</code>’s original value is only reachable from the trie. Although it’s still possible to call <code>keyalesce</code> with <code>[1, 2, 5, 7]</code>, <code>keyalesce</code> can actually prune the key and its associated value sequence because there isn’t any code that depends on receiving that specific key anymore. <code>keyalesce</code> doesn’t need to return the same unique key for a given value sequence. It only needs to prevent multiple keys existing <em>simultaneously</em> for the same value sequence.</p><p>Similarly to the handling of object sequence values, I made the trie hold only weak references to created keys and pruned the trie when the keys are garbage-collected using <code>FinalizationRegistry</code>.</p><p>After pruning in this case the trie would look like so:</p><svg aria-roledescription="flowchart-v2" class="flowchart" id="mermaid-4" style="font-family:Kantumruy Pro;font-size:16px;fill:#333;max-width:376.109375px" viewBox="0 0 376.109 61.516" width="100%" xmlns="http://www.w3.org/2000/svg"><style>@keyframes edge-animation-frame{0%{stroke-dashoffset:0}}@keyframes dash{to{stroke-dashoffset:0}}#mermaid-4 .edge-thickness-normal{stroke-width:1px}#mermaid-4 .edge-pattern-solid{stroke-dasharray:0}#mermaid-4 .marker{fill:#0b0b0b;stroke:#0b0b0b}#mermaid-4 .marker.cross{stroke:#0b0b0b}#mermaid-4 svg{font-family:Kantumruy Pro;font-size:16px}#mermaid-4 p{margin:0}#mermaid-4 .label{font-family:Kantumruy Pro;color:#333}#mermaid-4 span{fill:#333;color:#333}#mermaid-4 .node circle,#mermaid-4 .node path,#mermaid-4 .node rect{fill:#fff4dd;stroke:#edb;stroke-width:1px}#mermaid-4 .node .label{text-align:center}#mermaid-4 .flowchart-link{stroke:#0b0b0b;fill:none}#mermaid-4 .edgeLabel{background-color:#fdd;text-align:center}#mermaid-4 .edgeLabel p{background-color:#fdd}#mermaid-4 .edgeLabel rect{opacity:.5;background-color:#fdd;fill:#fdd}#mermaid-4 .labelBkg{background-color:rgba(244,221,255,.5)}#mermaid-4 :root{--mermaid-font-family:Kantumruy Pro}</style><marker class="flowchart-v2 marker" id="mermaid-4_flowchart-v2-pointEnd" markerHeight="8" markerUnits="userSpaceOnUse" markerWidth="8" orient="auto" refX="5" refY="5" viewBox="0 0 10 10"><path class="arrowMarkerPath" d="m0 0 10 5-10 5z" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker" id="mermaid-4_flowchart-v2-pointStart" markerHeight="8" markerUnits="userSpaceOnUse" markerWidth="8" orient="auto" refX="4.5" refY="5" viewBox="0 0 10 10"><path class="arrowMarkerPath" d="m0 5 10 5V0z" style="stroke-width:1;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker" id="mermaid-4_flowchart-v2-circleEnd" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="11" refY="5" viewBox="0 0 10 10"><circle class="arrowMarkerPath" r="5" cx="5" cy="5" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker class="flowchart-v2 marker" id="mermaid-4_flowchart-v2-circleStart" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="-1" refY="5" viewBox="0 0 10 10"><circle class="arrowMarkerPath" r="5" cx="5" cy="5" style="stroke-width:1;stroke-dasharray:1,0"></circle></marker><marker class="flowchart-v2 marker cross" id="mermaid-4_flowchart-v2-crossEnd" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="12" refY="5.2" viewBox="0 0 11 11"><path class="arrowMarkerPath" d="m1 1 9 9m0-9-9 9" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><marker class="flowchart-v2 marker cross" id="mermaid-4_flowchart-v2-crossStart" markerHeight="11" markerUnits="userSpaceOnUse" markerWidth="11" orient="auto" refX="-1" refY="5.2" viewBox="0 0 11 11"><path class="arrowMarkerPath" d="m1 1 9 9m0-9-9 9" style="stroke-width:2;stroke-dasharray:1,0"></path></marker><g class="root"><g class="edgePaths"><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M53.453 30.758h46" data-edge="true" data-et="edge" data-id="L_R_1_0" data-points="W3sieCI6NTMuNDUzMTI1LCJ5IjozMC43NTc4MTI1fSx7IngiOjc4LjQ1MzEyNSwieSI6MzAuNzU3ODEyNX0seyJ4IjoxMDMuNDUzMTI1LCJ5IjozMC43NTc4MTI1fV0=" id="L_R_1_0" marker-end="url(#mermaid-4_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M124.266 30.758h46" data-edge="true" data-et="edge" data-id="L_1_2_0" data-points="W3sieCI6MTI0LjI2NTYyNSwieSI6MzAuNzU3ODEyNX0seyJ4IjoxNDkuMjY1NjI1LCJ5IjozMC43NTc4MTI1fSx7IngiOjE3NC4yNjU2MjUsInkiOjMwLjc1NzgxMjV9XQ==" id="L_1_2_0" marker-end="url(#mermaid-4_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M198.219 30.758h46" data-edge="true" data-et="edge" data-id="L_2_4_0" data-points="W3sieCI6MTk4LjIxODc1LCJ5IjozMC43NTc4MTI1fSx7IngiOjIyMy4yMTg3NSwieSI6MzAuNzU3ODEyNX0seyJ4IjoyNDguMjE4NzUsInkiOjMwLjc1NzgxMjV9XQ==" id="L_2_4_0" marker-end="url(#mermaid-4_flowchart-v2-pointEnd)"></path><path class="edge-pattern-solid edge-thickness-normal flowchart-link" d="M272.594 30.758h46" data-edge="true" data-et="edge" data-id="L_4_K1_0" data-points="W3sieCI6MjcyLjU5Mzc1LCJ5IjozMC43NTc4MTI1fSx7IngiOjI5Ny41OTM3NSwieSI6MzAuNzU3ODEyNX0seyJ4IjozMjIuNTkzNzUsInkiOjMwLjc1NzgxMjV9XQ==" id="L_4_K1_0" marker-end="url(#mermaid-4_flowchart-v2-pointEnd)"></path></g><g class="edgeLabels"><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_R_1_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_1_2_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_2_4_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g><g class="edgeLabel"><foreignObject height="0" width="0" class="label" data-id="L_4_K1_0"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:200px;text-align:center" xmlns="http://www.w3.org/1999/xhtml" class="labelBkg"><span class="edgeLabel"></span></div></foreignObject></g></g><g class="nodes"><g class="default node" transform="translate(113.86 30.758)" id="flowchart-1-2"><circle class="basic label-container" r="10.406"></circle><g class="label" transform="translate(-2.906 -12)"><rect></rect><foreignObject height="24" width="5.813"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>1</p></span></div></foreignObject></g></g><g class="default node" transform="translate(186.242 30.758)" id="flowchart-2-3"><circle class="basic label-container" r="11.977"></circle><g class="label" transform="translate(-4.477 -12)"><rect></rect><foreignObject height="24" width="8.953"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>2</p></span></div></foreignObject></g></g><g class="default node" transform="translate(260.406 30.758)" id="flowchart-4-4"><circle class="basic label-container" r="12.188"></circle><g class="label" transform="translate(-4.688 -12)"><rect></rect><foreignObject height="24" width="9.375"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>4</p></span></div></foreignObject></g></g><g class="default node" transform="translate(30.727 30.758)" id="flowchart-R-0"><circle class="basic label-container" r="22.727"></circle><g class="label" transform="translate(-15.227 -12)"><rect></rect><foreignObject height="24" width="30.453"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>root</p></span></div></foreignObject></g></g><g class="default node" transform="translate(345.352 30.758)" id="flowchart-K1-1"><circle class="basic label-container" r="22.758"></circle><g class="label" transform="translate(-15.258 -12)"><rect></rect><foreignObject height="24" width="30.516"><div style="display:table-cell;white-space:nowrap;line-height:1.5;max-width:400px;text-align:center" xmlns="http://www.w3.org/1999/xhtml"><span class="nodeLabel"><p>key1</p></span></div></foreignObject></g></g></g></g></svg><div class="relative my-10 rounded-md border-l-4 p-8 border-l-blue-500 bg-blue-50" role="note"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;%23374151&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M11.25%2011.25l.041-.02a.75.75%200%20011.063.852l-.708%202.836a.75.75%200%20001.063.853l.041-.021M21%2012a9%209%200%2011-18%200%209%209%200%200118%200zm-9-3.75h.008v.008H12V8.25z&#x27;%20/%3e%3c/svg%3e" alt="" class="not-prose absolute top-0 left-0 m-0 box-content size-9 -translate-x-1/2 -translate-y-[45%] rounded-full border-4 border-blue-50 bg-blue-50"/><header class="pb-2 text-base font-semibold uppercase">Note</header><div class="*:first:mt-0 *:last:mb-0"><p>Although <code>1</code> was in the sequence it was not pruned because it is still needed for another sequence.</p></div></div><p>In summary, the trie is pruned whenever object sequence values or keys have only weak references to them.</p><h4 id="is-it-actually-safe-to-clean-up-unreachable-keys" class="group relative"><a data-discover="true" href="#is-it-actually-safe-to-clean-up-unreachable-keys" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Is it actually safe to clean up unreachable keys? permalink"/></a>Is it actually safe to clean up unreachable keys?</h4><p>You might think up the following scenario, which shows we can’t really know whether “any code depends on receiving that specific key anymore”:</p><ol><li>Use <code>keyalesce</code> to create a key from a sequence of primitive values</li><li>Add some properties to the created key</li><li>Depend on those added properties existing throughout your code</li><li>Let the key become unreachable</li><li>Get confused when <code>keyalesce</code> returns a key without those properties for the same sequence of primitive values from before, causing your code to misbehave</li></ol><p><code>keyalesce</code> prevents this scenario by <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze" rel="nofollow">freezing</a> the created key, which makes it impossible to depend on the contents of the key. The key is a completely opaque object.</p><p>The correct alternative to setting properties on the created key is to, well, use it as a key! You can use it as a key in a:</p><ul><li><code>Map</code>; if you want the key’s associated value to exist as long as the <code>Map</code> is reachable. Beware of memory leaks in this case!</li><li><code>WeakMap</code>; if you want the key’s associated value to exist only as long as the key is reachable by the rules explained before</li></ul><h2 id="go-use-it" class="group relative"><a data-discover="true" href="#go-use-it" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Go use it! permalink"/></a>Go use it!</h2><p>Install <a href="https://github.com/TomerAberbach/keyalesce" rel="nofollow"><code>keyalesce</code></a>:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#ffcb6b">$</span><span style="color:#c3e88d"> npm</span><span style="color:#c3e88d"> i</span><span style="color:#c3e88d"> keyalesce</span></span></code></pre><p>And import it:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#89ddff;font-style:italic">import</span><span style="color:#babed8"> keyalesce </span><span style="color:#89ddff;font-style:italic">from</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">keyalesce</span><span style="color:#89ddff">&#x27;</span></span>
<span class="line"></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> hangouts </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> new</span><span style="color:#82aaff"> Set</span><span style="color:#babed8">()</span></span>
<span class="line"></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> createHangoutKey </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> (</span><span style="color:#babed8;font-style:italic">person1</span><span style="color:#89ddff">,</span><span style="color:#babed8;font-style:italic"> person2</span><span style="color:#89ddff">)</span><span style="color:#c792ea"> =&gt;</span></span>
<span class="line"><span style="color:#82aaff">  keyalesce</span><span style="color:#babed8">([person1</span><span style="color:#89ddff">,</span><span style="color:#babed8"> person2]</span><span style="color:#89ddff">.</span><span style="color:#82aaff">sort</span><span style="color:#babed8">())</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> hangOut </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> (</span><span style="color:#babed8;font-style:italic">person1</span><span style="color:#89ddff">,</span><span style="color:#babed8;font-style:italic"> person2</span><span style="color:#89ddff">)</span><span style="color:#c792ea"> =&gt;</span></span>
<span class="line"><span style="color:#babed8">  hangouts</span><span style="color:#89ddff">.</span><span style="color:#82aaff">add</span><span style="color:#babed8">(</span><span style="color:#82aaff">createHangoutKey</span><span style="color:#babed8">(person1</span><span style="color:#89ddff">,</span><span style="color:#babed8"> person2))</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> didTheyHangOut </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> (</span><span style="color:#babed8;font-style:italic">person1</span><span style="color:#89ddff">,</span><span style="color:#babed8;font-style:italic"> person2</span><span style="color:#89ddff">)</span><span style="color:#c792ea"> =&gt;</span></span>
<span class="line"><span style="color:#babed8">  hangouts</span><span style="color:#89ddff">.</span><span style="color:#82aaff">has</span><span style="color:#babed8">(</span><span style="color:#82aaff">createHangoutKey</span><span style="color:#babed8">(person1</span><span style="color:#89ddff">,</span><span style="color:#babed8"> person2))</span></span>
<span class="line"></span>
<span class="line"><span style="color:#82aaff">hangOut</span><span style="color:#babed8">(</span><span style="color:#89ddff">`</span><span style="color:#c3e88d">Tomer</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Samuel</span><span style="color:#89ddff">`</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#82aaff">hangOut</span><span style="color:#babed8">(</span><span style="color:#89ddff">`</span><span style="color:#c3e88d">Tomer</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Amanda</span><span style="color:#89ddff">`</span><span style="color:#babed8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#82aaff">didTheyHangOut</span><span style="color:#babed8">(</span><span style="color:#89ddff">`</span><span style="color:#c3e88d">Tomer</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Samuel</span><span style="color:#89ddff">`</span><span style="color:#babed8">))</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#82aaff">didTheyHangOut</span><span style="color:#babed8">(</span><span style="color:#89ddff">`</span><span style="color:#c3e88d">Samuel</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Tomer</span><span style="color:#89ddff">`</span><span style="color:#babed8">))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#82aaff">didTheyHangOut</span><span style="color:#babed8">(</span><span style="color:#89ddff">`</span><span style="color:#c3e88d">Tomer</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Amanda</span><span style="color:#89ddff">`</span><span style="color:#babed8">))</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#82aaff">didTheyHangOut</span><span style="color:#babed8">(</span><span style="color:#89ddff">`</span><span style="color:#c3e88d">Amanda</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Tomer</span><span style="color:#89ddff">`</span><span style="color:#babed8">))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#82aaff">didTheyHangOut</span><span style="color:#babed8">(</span><span style="color:#89ddff">`</span><span style="color:#c3e88d">Samuel</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Amanda</span><span style="color:#89ddff">`</span><span style="color:#babed8">))</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#82aaff">didTheyHangOut</span><span style="color:#babed8">(</span><span style="color:#89ddff">`</span><span style="color:#c3e88d">Amanda</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">Samuel</span><span style="color:#89ddff">`</span><span style="color:#babed8">))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false</span></span></code></pre><section class="footnotes" data-footnotes=""><h2 id="footnote-label" class="sr-only">Footnotes</h2><ol><li id="fn-1"><p>Two value sequences are considered equal if each of their values are equal using the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#key_equality" rel="nofollow">SameValueZero algorithm</a>. <a href="#fnref-1" aria-label="Back to reference 1" class="data-footnote-backref">↩</a></p></li></ol></section>]]></content:encoded>
                <description><![CDATA[Have you ever wanted to use tuples or objects for the keys of a Map or the values of a Set? It's a very common question because the following code doesn't do what you might expect: The code behaves…]]></description>
                <guid isPermaLink="true">https://tomeraberba.ch/the-making-of-keyalesce</guid>
                <pubDate>Sun, 02 Jul 2023 00:00:00 GMT</pubDate>
                <lastBuildDate>Sat, 16 Nov 2024 00:00:00 GMT</lastBuildDate>
              </item>
            
              <item>
                <title><![CDATA[Avoid Layout Shifts Caused by Web Fonts With PostCSS Fontpie]]></title>
                <link>https://tomeraberba.ch/avoid-layout-shifts-caused-by-web-fonts-with-postcss-fontpie</link>
                <category><![CDATA[code]]></category><category><![CDATA[fonts]]></category><category><![CDATA[javascript]]></category><category><![CDATA[libraries]]></category><category><![CDATA[performance]]></category><category><![CDATA[postcss]]></category>
                <content:encoded><![CDATA[<p>When your CSS references a web font before it finishes downloading, the browser renders text using a fallback system font instead, causing a <a href="https://web.dev/cls" rel="nofollow">layout shift</a> if the text container’s height changes once it’s rendered with the web font.</p><p><a href="https://simonhearne.com/2021/layout-shifts-webfonts#optimise-font-files" rel="nofollow">Optimizing your font files</a> and using an <a href="https://simonhearne.com/2021/layout-shifts-webfonts#deliver-your-fonts-fast" rel="nofollow">intelligent font loading strategy</a> can eliminate layout shifts, but only if the web font is not immediately used on the page. Otherwise you have to ensure the text container’s height doesn’t change. <a href="https://github.com/pixel-point/fontpie" rel="nofollow">Fontpie</a> can help with this. It’s a tool for generating CSS that adjusts a fallback font’s metrics so it takes up the same amount of space as your web font. The result is no layout shifts even if the font is immediately used!</p><p>Part of what makes Fontpie great is that it’s framework agnostic, but that also means it’s a bit manual to use and I wanted something I could easily integrate into this website’s build. I was already using <a href="https://github.com/postcss/postcss" rel="nofollow">PostCSS</a> so I decided to make a Fontpie PostCSS plugin. The result is <a href="https://github.com/TomerAberbach/postcss-fontpie" rel="nofollow"><code>postcss-fontpie</code></a>. And now generating fallback font metrics for my web fonts is as easy as adding the following to my <a href="https://github.com/TomerAberbach/website/blob/main/postcss.config.js" rel="nofollow">PostCSS config</a>:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#89ddff"> {</span><span style="color:#babed8"> join </span><span style="color:#89ddff">}</span><span style="color:#89ddff"> =</span><span style="color:#82aaff"> require</span><span style="color:#babed8">(</span><span style="color:#89ddff">`</span><span style="color:#c3e88d">path</span><span style="color:#89ddff">`</span><span style="color:#babed8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#89ddff">module.exports</span><span style="color:#89ddff"> =</span><span style="color:#89ddff"> {</span></span>
<span class="line"><span style="color:#f07178">  plugins</span><span style="color:#89ddff">:</span><span style="color:#babed8"> [</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">    // ...</span></span>
<span class="line"><span style="color:#82aaff">    require</span><span style="color:#babed8">(</span><span style="color:#89ddff">`</span><span style="color:#c3e88d">postcss-fontpie</span><span style="color:#89ddff">`</span><span style="color:#babed8">)(</span><span style="color:#89ddff">{</span></span>
<span class="line"><span style="color:#f07178">      fontTypes</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> {</span></span>
<span class="line"><span style="color:#f07178">        dm</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">mono</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span></span>
<span class="line"><span style="color:#89ddff">        &#x27;</span><span style="color:#f07178">Kantumruy Pro</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">sans-serif</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span></span>
<span class="line"><span style="color:#89ddff">      },</span></span>
<span class="line"><span style="color:#82aaff">      srcUrlToFilename</span><span style="color:#89ddff">:</span><span style="color:#babed8;font-style:italic"> url</span><span style="color:#c792ea"> =&gt;</span><span style="color:#82aaff"> join</span><span style="color:#babed8">(__dirname</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> `</span><span style="color:#c3e88d">src/styles</span><span style="color:#89ddff">`</span><span style="color:#89ddff">,</span><span style="color:#babed8"> url)</span><span style="color:#89ddff">,</span></span>
<span class="line"><span style="color:#89ddff">    }</span><span style="color:#babed8">)</span><span style="color:#89ddff">,</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">    // ...</span></span>
<span class="line"><span style="color:#babed8">  ]</span><span style="color:#89ddff">,</span></span>
<span class="line"><span style="color:#89ddff">}</span></span></code></pre><p>You can see the generated fallback font metrics in <a href="/assets/fonts-BdHI-ET-.css">this website’s CSS</a> and their effects in the following before-and-after GIFs:</p><div class="max-w-full child:flex-1 flex flex-wrap"><div class="max-w-full gif min-w-[min(400px,100%)]"><video aria-label="Website&#x27;s font load causing a layout shift" aria-roledescription="gif" autoPlay="" class="m-0" loop="" muted="" playsInline="" role="img"><source src="/assets/without-postcss-fontpie-DO8-_QzM.webm" type="video/webm"/><source src="/assets/without-postcss-fontpie-Dcum6aj4.mp4" type="video/mp4"/></video></div><div class="max-w-full gif min-w-[min(400px,100%)]"><video aria-label="Website&#x27;s font load with no layout shift" aria-roledescription="gif" autoPlay="" class="m-0" loop="" muted="" playsInline="" role="img"><source src="/assets/with-postcss-fontpie-DQZjamvT.webm" type="video/webm"/><source src="/assets/with-postcss-fontpie-CPXovj45.mp4" type="video/mp4"/></video></div></div><p>Check out <a href="https://github.com/TomerAberbach/postcss-fontpie#usage" rel="nofollow"><code>postcss-fontpie</code>’s usage example</a> to use it in your own website!</p>]]></content:encoded>
                <description><![CDATA[When your CSS references a web font before it finishes downloading, the browser renders text using a fallback system font instead, causing a layout shift if the text container's height changes once…]]></description>
                <guid isPermaLink="true">https://tomeraberba.ch/avoid-layout-shifts-caused-by-web-fonts-with-postcss-fontpie</guid>
                <pubDate>Wed, 24 May 2023 00:00:00 GMT</pubDate>
                
              </item>
            
              <item>
                <title><![CDATA[What I Worked On at Google]]></title>
                <link>https://tomeraberba.ch/what-i-worked-on-at-google</link>
                <category><![CDATA[ai]]></category><category><![CDATA[code]]></category><category><![CDATA[docs]]></category><category><![CDATA[drawings]]></category><category><![CDATA[drive]]></category><category><![CDATA[engineering]]></category><category><![CDATA[google]]></category><category><![CDATA[markdown]]></category><category><![CDATA[resume]]></category><category><![CDATA[sheets]]></category><category><![CDATA[sites]]></category><category><![CDATA[slides]]></category>
                <content:encoded><![CDATA[<link rel="preload" as="image" href="/assets/docs-markdown-import-export-hn-CyHQy7mj.png"/><p>Naturally this list only includes projects I’m allowed to talk about! For many projects I was not the only person who worked on it. Projects I worked on as part of my <a href="https://builtin.com/software-engineering-perspectives/20-percent-time" rel="nofollow">20% time</a> are labeled with <strong><em>20%</em></strong>. The projects are ordered from most to least recent.</p><ul><li><p>Spearheaded the development of high-fidelity <a href="https://workspaceupdates.googleblog.com/2024/07/import-and-export-markdown-in-google-docs.html" rel="nofollow">Markdown import and export for Docs</a>, enhancing its compatibility with numerous content management systems and resulting in a user importing or exporting Markdown every second. <strong><em>20%</em></strong></p><p>Several news outlets took note:</p><ul><li><a href="https://arstechnica.com/gadgets/2024/07/real-actual-markdown-support-is-arriving-in-google-docs-not-a-moment-too-soon" rel="nofollow">Ars Technica: Real, actual Markdown support is arriving in Google Docs, not a moment too soon</a></li><li><a href="https://www.howtogeek.com/google-docs-markdown-import-export" rel="nofollow">How-To Geek: Google Docs Can Now Import and Export Markdown</a></li></ul><p>And <a href="https://news.ycombinator.com/item?id=40982118" rel="nofollow">Hacker News <em>definitely</em> noticed</a>.</p><p><img alt="The Hacker News homepage with &quot;Import and Export Markdown in Google Docs&quot; as the top post" src="/assets/docs-markdown-import-export-hn-CyHQy7mj.png"/></p></li><li><p>Co-led a foundational rearchitecture of the Docs backend, frontend, and data model to implement <a href="https://workspaceupdates.googleblog.com/2024/10/tabs-in-google-docs.html" rel="nofollow">tabs in Docs</a>, including updating all cross-cutting features, resulting in ~1.75 tabs on average for recently opened documents. The Verge took note: <a href="https://www.theverge.com/2024/10/8/24265025/google-docs-tabs-organization-feature-availability" rel="nofollow">“Google Docs is making it much easier to organize information”</a>.</p></li><li><p>Delighted users with some easter eggs in Docs. Try typing <a href="https://www.instagram.com/reel/Cx3jQFbL072" rel="nofollow"><code>@tictactoe</code></a> or <a href="https://www.instagram.com/p/C2g0I3dt3LV" rel="nofollow"><code>@fourinarow</code></a>! <strong><em>20%</em></strong></p></li><li><p>Integrated Gemini into Docs to enable <a href="https://workspace.google.com/blog/product-announcements/generative-ai" rel="nofollow">GenAI content generation and rewrites</a>, complete with headings, lists, tables, and other rich content, as a member of a large team, resulting in ~150,000 weekly usages.</p><p><em>Many</em> news outlets took note:</p><ul><li><a href="https://www.forbes.com/sites/qai/2023/03/15/google-stock-jumps-as-it-unveils-new-ai-powered-tools-for-workspace" rel="nofollow">Forbes: Google Stock Jumps As It Puts Artificial Intelligence Into Google Docs</a></li><li><a href="https://fortune.com/2023/03/14/gpt-4-debuts-and-google-beats-microsoft-in-race-to-add-generative-a-i-to-consumer-office-tools" rel="nofollow">Fortune: GPT-4 debuts and Google beats Microsoft in race to add generative A.I. to consumer office tools</a></li><li><a href="https://www.reuters.com/technology/google-unveils-magic-wand-draft-documents-ai-race-tightens-2023-03-14" rel="nofollow">Reuters: Google unveils ‘magic wand’ to draft documents as AI race tightens</a></li><li><a href="https://www.theverge.com/2023/3/14/23639273/google-ai-features-docs-gmail-slides-sheets-workspace" rel="nofollow">The Verge: Google announces AI features in Gmail, Docs, and more to rival Microsoft</a></li><li><a href="https://www.usatoday.com/story/tech/2023/03/14/google-ai-announcement-gmail-docs/11470811002" rel="nofollow">USA Today: Google unveils new features for Docs, Gmail and more amid AI race</a></li><li><a href="https://www.washingtonpost.com/technology/2023/03/14/google-workspace-ai" rel="nofollow">The Washington Post: Google is adding AI to its work apps. Here’s what that means.</a></li></ul></li><li><p>Jointly implemented <a href="https://workspaceupdates.googleblog.com/2022/12/format-display-code-google-docs.html" rel="nofollow">code block support in Docs, including automatic Markdown detection and conversion</a>, resulting in ~90,000 weekly code block insertions, with 25% from Markdown conversion. The Verge took note: <a href="https://www.theverge.com/2022/12/14/23509936/google-docs-code-blocks-smart-canvas" rel="nofollow">“Google’s making code formatting a breeze in Docs”</a>.</p></li><li><p>Independently empowered Docs users with improved data organization by developing an option to <a href="https://workspaceupdates.googleblog.com/2022/10/split-table-cells-in-google-docs.html" rel="nofollow">split table cells</a>, leading to ~1.15 million weekly cell splits. <strong><em>20%</em></strong></p></li><li><p>Boosted Google Docs collaboration by 8% by developing a <a href="https://workspaceupdates.googleblog.com/2022/07/edit-notifications-for-document-content-changes.html" rel="nofollow">real-time email alert system</a>, complete with subscription settings and document diffs for recent edits, that sends ~300,000 weekly emails, as a member of a 3-person team.</p></li><li><p>Increased developer productivity by implementing <a href="https://workspaceupdates.googleblog.com/2022/03/compose-with-markdown-in-google-docs-on.html" rel="nofollow">automatic Markdown detection and conversion for Docs, Slides, and Drawings</a><sup><a href="#fn-1" aria-describedby="footnote-label" data-footnote-ref="" id="fnref-1">1</a></sup>, resulting in ~1.2 million automatic weekly conversions. <strong><em>20%</em></strong></p><p>Several news outlets took note:</p><ul><li><a href="https://www.wired.com/story/how-to-use-markdown-google-docs" rel="nofollow">WIRED: “How to Use Markdown in Google Docs”</a></li><li><a href="https://www.theverge.com/2022/3/29/23002138/google-docs-markdown-support-formatting-update" rel="nofollow">The Verge: ”# Google Docs is getting more Markdown support”</a></li><li><a href="https://techcrunch.com/2022/03/30/google-adds-limited-markdown-support-to-google-docs" rel="nofollow">TechCrunch: “Google adds limited Markdown support to Google Docs”</a></li></ul><p>And <a href="https://x.com/TomerAberbach/status/1508895335200043021" rel="nofollow">tech Twitter <em>definitely</em> noticed</a>.</p></li><li><p>Simplified Docs, Slides, and Drawings authoring with shared prefix substitution rule support, and <a href="https://x.com/googledocs/status/1471555730364846083" rel="nofollow">en- and em-dash substitution</a>, resulting in a 45% increase in substitutions and no change in undo rates. <strong><em>20%</em></strong></p></li><li><p><a href="https://workspaceupdates.googleblog.com/2021/09/comment-size-increasing-in-google-docs.html" rel="nofollow">Increased the width of comment threads in Docs</a>. They were <em>way</em> too narrow and it was bothering me! <span aria-label="grinning face with sweat" role="img">😅</span> <strong><em>20%</em></strong></p></li><li><p>Developed <a href="https://blog.google/products/gmail/take-action-and-stay-up-to-date-with-dynamic-email-in-gmail" rel="nofollow">dynamic</a> <a href="https://support.google.com/drive/answer/2494822" rel="nofollow">Drive sharing emails</a> that automatically update with the latest thumbnail, owner, and edit details via a high-performance endpoint handling 300 queries per second, resulting in 6% fewer short sessions from the emails.</p></li><li><p>Expanded <a href="https://9to5google.com/2019/09/18/google-docs-link-previews" rel="nofollow">link previews in Docs, Sheets, Slides, and Drawings to links in comment threads</a>. Previously, they only appeared for links in the editor itself. <strong><em>20%</em></strong></p></li><li><p>Streamlined finding new and important comments in Docs with <a href="https://workspaceupdates.googleblog.com/2021/02/improvements-for-locating-new-comments-important-conversations-google-docs.html" rel="nofollow">comment badging and filtering</a>, boosting reply creation and comment resolutions by 2%. USA TODAY took note: <a href="https://www.usatoday.com/story/tech/2021/02/23/google-docs-comments-feature-make-collaboration-easier/4554035001" rel="nofollow">“Google Docs upgrades comments feature to make collaboration easier”</a>.</p></li><li><p>Interned on the <a href="https://sites.google.com" rel="nofollow">Sites</a> team in the Summer of 2019 and <a href="https://workspaceupdates.googleblog.com/2019/11/feedback-google-sites.html" rel="nofollow">enabled site viewers to provide feedback on published Sites</a>.</p></li></ul><section class="footnotes" data-footnotes=""><h2 class="sr-only" id="footnote-label">Footnotes</h2><ol><li id="fn-1"><p>Yes, Google has an editor called <a href="https://drawings.google.com" rel="nofollow">Drawings</a>. <a href="#fnref-1" aria-label="Back to reference 1" class="data-footnote-backref">↩</a></p></li></ol></section>]]></content:encoded>
                <description><![CDATA[Naturally this list only includes projects I'm allowed to talk about! For many projects I was not the only person who worked on it. Projects I worked on as part of my 20% time are labeled with 20%.…]]></description>
                <guid isPermaLink="true">https://tomeraberba.ch/what-i-worked-on-at-google</guid>
                <pubDate>Sun, 23 Apr 2023 00:00:00 GMT</pubDate>
                <lastBuildDate>Wed, 09 Oct 2024 00:00:00 GMT</lastBuildDate>
              </item>
            
              <item>
                <title><![CDATA[Complexity]]></title>
                <link>https://tomeraberba.ch/complexity</link>
                <category><![CDATA[music]]></category><category><![CDATA[piano]]></category><category><![CDATA[tracks]]></category>
                <content:encoded><![CDATA[<p>I composed, produced, and released an instrumental piano track!</p><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowFullScreen="" frameBorder="0" height="150" src="https://www.youtube.com/embed/Mzu5StfzBw8?feature=oembed" title="Complexity" width="200"></iframe><p>You can also find the track on <a href="https://open.spotify.com/track/5zsCMG0tDwifrXXk9HLEDI" rel="nofollow">Spotify</a>, <a href="https://music.apple.com/us/album/complexity/1663586182" rel="nofollow">Apple Music</a>, <a href="https://music.youtube.com/watch?v=Mzu5StfzBw8" rel="nofollow">YouTube Music</a>, and other music streaming services. Special thanks to Emily Kazenmayer for the album cover!</p>]]></content:encoded>
                <description><![CDATA[I composed, produced, and released an instrumental piano track! https://www.youtube.com/watch?v=Mzu5StfzBw8 You can also find the track on Spotify, Apple Music, YouTube Music, and other music…]]></description>
                <guid isPermaLink="true">https://tomeraberba.ch/complexity</guid>
                <pubDate>Sat, 14 Jan 2023 00:00:00 GMT</pubDate>
                
              </item>
            
              <item>
                <title><![CDATA[The Hero Archetype]]></title>
                <link>https://tomeraberba.ch/the-hero-archetype</link>
                <category><![CDATA[music]]></category><category><![CDATA[piano]]></category><category><![CDATA[tracks]]></category>
                <content:encoded><![CDATA[<p>I composed, produced, and released an instrumental piano track!</p><iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowFullScreen="" frameBorder="0" height="150" src="https://www.youtube.com/embed/6eAIubjV4qw?feature=oembed" title="The Hero Archetype" width="200"></iframe><p>You can also find the track on <a href="https://open.spotify.com/track/1DucGzpQKsr6qrQXZx5E4M" rel="nofollow">Spotify</a>, <a href="https://music.apple.com/us/album/the-hero-archetype-single/1644752209" rel="nofollow">Apple Music</a>, <a href="https://music.youtube.com/watch?v=6eAIubjV4qw" rel="nofollow">YouTube Music</a>, and other music streaming services. Special thanks to <a href="https://jillmarbach.com" rel="nofollow">Jill Marbach</a> for the album cover!</p>]]></content:encoded>
                <description><![CDATA[I composed, produced, and released an instrumental piano track! https://www.youtube.com/watch?v=6eAIubjV4qw You can also find the track on Spotify, Apple Music, YouTube Music, and other music…]]></description>
                <guid isPermaLink="true">https://tomeraberba.ch/the-hero-archetype</guid>
                <pubDate>Fri, 02 Sep 2022 00:00:00 GMT</pubDate>
                
              </item>
            
              <item>
                <title><![CDATA[Publications]]></title>
                <link>https://tomeraberba.ch/publications</link>
                <category><![CDATA[biology]]></category><category><![CDATA[code]]></category><category><![CDATA[google]]></category><category><![CDATA[publications]]></category><category><![CDATA[resume]]></category>
                <content:encoded><![CDATA[<p>I’ve had a few publications over the years. The publications are ordered from most to least recent.</p><ul><li><p><a href="https://patents.google.com/patent/US20240330580A1" rel="nofollow">Generation of Personalized and Structured Content Using a Collaborative Online Generator</a> · October 3, 2024</p><blockquote><p>Systems and methods for generating personalized and structured content using a collaborative generator provide a user interface to a user computing system and receive a prompt from the user computing system via the user interface. The systems and methods provide the prompt to a generative model, with the generative model being a machine-learned model trained to process language input prompts to generate a language output. The systems and methods receive a generative output generated by the generative model in response to the prompt. Additionally, the systems and methods generate a modified output by modifying the generative output based at least in part on historical user data for a user associated with the prompt, and then provide the modified output via the user interface.</p></blockquote><p>The techniques described in this patent were used for <a href="/what-i-worked-on-at-google">integrating Gemini into Google Docs</a>.</p></li><li><p><a href="https://www.tdcommons.org/dpubs_series/7077" rel="nofollow">Language Agnostic Incremental Real-time Code Highlighting</a> · June 5, 2024</p><blockquote><p>This disclosure describes techniques of fast, real-time highlighting of code snippets with low algorithmic complexity. The techniques are suitable to enable highlighting of code sections within relatively lightweight environments such as general-purpose word processors. Delimited ranges of characters within code are identified. Changes within delimited ranges are tracked using a sparse array. The use of a sparse array enables operations on the delimited range to be tracked at a low computational complexity. The highlighted color of a word is the color of its delimited range. The color highlighting maintains real-time synchronization with edits being made simultaneously on a document by collaborating developers. The overhead to define a language is kept low, such that highlighting can be provided without building knowledge of the entire syntax and grammar of the language into the code highlighting implementation.</p></blockquote><p>The techniques described in this defensive publication were used for <a href="/what-i-worked-on-at-google">adding code blocks to Google Docs</a>.</p></li><li><p><a href="https://pubmed.ncbi.nlm.nih.gov/38669847" rel="nofollow">Synthesis cost-optimal targeted mutant protein libraries</a> · April 18, 2024</p><blockquote><p>Protein variant libraries produced by site-directed mutagenesis are a useful tool utilized by protein engineers to explore variants with potentially improved properties, such as activity and stability. These libraries are commonly built by selecting residue positions and alternative beneficial mutations for each position. All possible combinations are then constructed and screened, by incorporating degenerate codons at mutation sites. These degenerate codons often encode additional unwanted amino acids or even STOP codons. Our study aims to take advantage of annealing based recombination of oligonucleotides during synthesis and utilize multiple degenerate codons per mutation site to produce targeted protein libraries devoid of unwanted variants. Toward this goal we created an algorithm to calculate the minimum number of degenerate codons necessary to specify any given amino acid set, and a dynamic programming method that uses this algorithm to optimally partition a DNA target sequence with degeneracies into overlapping oligonucleotides, such that the total cost of synthesis of the target mutant protein library is minimized. Computational experiments show that, for a modest increase in DNA synthesis costs, beneficial variant yields in produced mutant libraries are increased by orders of magnitude, an effect particularly pronounced in large combinatorial libraries.</p></blockquote></li><li><p><a href="https://patents.google.com/patent/US20240119224A1" rel="nofollow">Table cell splitting in an online document editor</a> · April 11, 2024</p><blockquote><p>Techniques are described herein for table cell splitting in an online document editor. A method includes: responsive to a request to split a cell in a table, determining a target number of rows and a target number of columns, automatically inserting rows adjacent to rows of the cell to reach the target number of rows, automatically inserting columns adjacent to columns of the cell to reach the target number of columns, and automatically merging groups of cells within an initial boundary of the cell, each group spanning a determined number of rows per group and a determined number of columns per group.</p></blockquote><p>The techniques described in this patent were used for <a href="/what-i-worked-on-at-google">adding table cell splitting to Google Docs</a>.</p></li><li><p><a href="https://www.tdcommons.org/dpubs_series/5207" rel="nofollow">Language Agnostic Code Highlighting in Word Processors</a> · June 16, 2022</p><blockquote><p>This disclosure describes techniques for highlighting code snippets included in text documents edited via a word processor. A text block containing code is received and analyzed using a tokenizer to identify specific words included in the text block. The words are classified by the tokenizer into a finite set of types (categories) by matching the words with a list of words defined for different computer languages. Words or characters are colorized based on whether the word is a language specific reserved keyword or a user-defined identifier. Multiple coding languages can be supported, with low maintenance, since only the active dictionary of reserved words needs to be updated when adding a language. The techniques can support live updates, highlighting code even as the user enters text. Incremental highlighting can be implemented with relatively minimal additional effort by analyzing only a small block of code near the altered text character(s).</p></blockquote><p>The techniques described in this defensive publication were used for <a href="/what-i-worked-on-at-google">adding code blocks to Google Docs</a>.</p></li><li><p><a href="https://par.nsf.gov/servlets/purl/10285027" rel="nofollow">Decodon Calculator: Degenerate Codon Set Design for Protein Variant Libraries</a> · August 1, 2020</p><blockquote><p>We have designed and implemented an algorithm that, given any set of amino acids, produces the minimum number of decodons necessary to code for exactly this set, i.e. without coding for extraneous amino acids or STOP codons. There are 15 nucleotide codes (“letters”), ranging from the completely unambiguous A, C, G and T representing a single nucleotide, to the completely ambiguous N representing all 4 nucleotides. There are 153 = 3, 375 decodons that can be assembled from this 15-letter alphabet of ambiguous codes, compared to the 43 = 64 codons that can be constructed from the standard 4-letter alphabet of unambiguous nucleotides.</p></blockquote></li></ul>]]></content:encoded>
                <description><![CDATA[I've had a few publications over the years. The publications are ordered from most to least recent. Generation of Personalized and Structured Content Using a Collaborative Online Generator · October…]]></description>
                <guid isPermaLink="true">https://tomeraberba.ch/publications</guid>
                <pubDate>Tue, 16 Aug 2022 00:00:00 GMT</pubDate>
                <lastBuildDate>Thu, 03 Oct 2024 00:00:00 GMT</lastBuildDate>
              </item>
            
              <item>
                <title><![CDATA[Checking for the Absence of a Value in JavaScript]]></title>
                <link>https://tomeraberba.ch/checking-for-the-absence-of-a-value-in-javascript</link>
                <category><![CDATA[code]]></category><category><![CDATA[javascript]]></category>
                <content:encoded><![CDATA[<p>JavaScript has a lot of similar-looking ways to check for the absence of a value:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(value </span><span style="color:#89ddff">==</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(value </span><span style="color:#89ddff">==</span><span style="color:#89ddff"> undefined</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> undefined</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> undefined</span><span style="color:#89ddff"> ||</span><span style="color:#babed8"> value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">typeof</span><span style="color:#babed8"> value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">undefined</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">typeof</span><span style="color:#babed8"> value </span><span style="color:#89ddff">==</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">undefined</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">typeof</span><span style="color:#babed8"> value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">undefined</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff"> ||</span><span style="color:#babed8"> value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">typeof</span><span style="color:#babed8"> value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">undefined</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff"> ||</span><span style="color:#babed8"> value </span><span style="color:#89ddff">==</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">typeof</span><span style="color:#babed8"> value </span><span style="color:#89ddff">==</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">undefined</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff"> ||</span><span style="color:#babed8"> value </span><span style="color:#89ddff">==</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">typeof</span><span style="color:#babed8"> value </span><span style="color:#89ddff">==</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">undefined</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff"> ||</span><span style="color:#babed8"> value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">!</span><span style="color:#babed8">value)</span></span></code></pre><p>Which one is right?</p><h2 id="absent-values" class="group relative"><a data-discover="true" href="#absent-values" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Absent values permalink"/></a>Absent values</h2><p>JavaScript has two representations of an absent value.</p><h3 id="undefined" class="group relative"><a data-discover="true" href="#undefined" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Undefined permalink"/></a>Undefined</h3><p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined" rel="nofollow"><code>undefined</code></a> is a JavaScript primitive type. The <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof" rel="nofollow"><code>typeof</code></a> operator returns <code>&#x27;undefined&#x27;</code> for <code>undefined</code>:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">typeof</span><span style="color:#89ddff"> undefined</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; undefined</span></span></code></pre><p>The value of a declared unassigned variable is <code>undefined</code>:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">let</span><span style="color:#babed8"> x</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(x)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; undefined</span></span></code></pre><p>The access of an absent object property returns <code>undefined</code>:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> object </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {}</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(object</span><span style="color:#89ddff">.</span><span style="color:#babed8">absentProperty)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; undefined</span></span></code></pre><p>The return value of a function that doesn’t explicitly return is <code>undefined</code>:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">function</span><span style="color:#82aaff"> f</span><span style="color:#89ddff">()</span><span style="color:#89ddff"> {}</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#82aaff">f</span><span style="color:#babed8">())</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; undefined</span></span></code></pre><p>The <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void" rel="nofollow"><code>void</code></a> operator always returns <code>undefined</code><sup><a href="#fn-1" aria-describedby="footnote-label" data-footnote-ref="" id="fnref-1">1</a></sup>:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">void</span><span style="color:#f78c6c"> 0</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; undefined</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">void</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">hello</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; undefined</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">void</span><span style="color:#babed8"> (</span><span style="color:#f78c6c">3</span><span style="color:#89ddff"> +</span><span style="color:#f78c6c"> 2</span><span style="color:#babed8">))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; undefined</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">void</span><span style="color:#babed8"> (</span><span style="color:#676e95;font-style:italic">/* any expression */</span><span style="color:#babed8">))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; undefined</span></span></code></pre><p>Lastly, <code>undefined</code> is not a literal! It is a property of the <a href="https://developer.mozilla.org/en-US/docs/Glossary/Global_object" rel="nofollow">global object</a>, which always exists in the global scope.</p><h3 id="null" class="group relative"><a data-discover="true" href="#null" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Null permalink"/></a>Null</h3><p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/null" rel="nofollow"><code>null</code></a> is also a JavaScript primitive type, but <code>typeof</code> returns something unexpected for <code>null</code>:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">typeof</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; object</span></span></code></pre><p>Ideally <code>typeof null</code> would return <code>&#x27;null&#x27;</code>, but <a href="https://2ality.com/2013/10/typeof-null.html" rel="nofollow"><code>typeof null</code> has returned <code>&#x27;object&#x27;</code> since JavaScript’s inception</a> and it would <a href="https://web.archive.org/web/20160331031419/http://wiki.ecmascript.org:80/doku.php?id=harmony:typeof_null" rel="nofollow">break existing code if the behavior were changed now</a>.</p><p><code>null</code> does not appear as a “default” value in JavaScript in the same way that <code>undefined</code> does. Instead, developers typically make functions return <code>null</code> when an object can’t be found or constructed. For example, in browsers <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById" rel="nofollow"><code>document.getElementById</code></a> returns <code>null</code> if there’s no element in the document with the given ID:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(document</span><span style="color:#89ddff">.</span><span style="color:#82aaff">getElementById</span><span style="color:#babed8">(</span><span style="color:#89ddff">&#x27;</span><span style="color:#c3e88d">some-id-that-no-element-has</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; null</span></span></code></pre><p>Unlike <code>undefined</code>, <code>null</code> <em>is</em> a literal. It is not a property of the global object.</p><h2 id="equality" class="group relative"><a data-discover="true" href="#equality" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Equality permalink"/></a>Equality</h2><p>Now that we’ve covered <code>undefined</code> and <code>null</code>, let’s address the difference between <code>==</code> and <code>===</code>.</p><h3 id="strict" class="group relative"><a data-discover="true" href="#strict" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Strict permalink"/></a>Strict</h3><p>Strict equality is invoked using <code>===</code>. For two values, <code>a</code> and <code>b</code>, <code>a === b</code> evaluates to <code>true</code> if <code>a</code> and <code>b</code> have the same type and their values are equal. Otherwise, <code>a === b</code> evaluates to <code>false</code>:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#f78c6c">0</span><span style="color:#89ddff"> ===</span><span style="color:#f78c6c"> 0</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">&#x27;</span><span style="color:#c3e88d">hello!</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff"> ===</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">hello!</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">null</span><span style="color:#89ddff"> ===</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">undefined</span><span style="color:#89ddff"> ===</span><span style="color:#89ddff"> undefined</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#f78c6c">0</span><span style="color:#89ddff"> ===</span><span style="color:#f78c6c"> 5</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false (same types, but different values)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#f78c6c">0</span><span style="color:#89ddff"> ===</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">0</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false (different types)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#f78c6c">0</span><span style="color:#89ddff"> ===</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">hello!</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false (different types)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">null</span><span style="color:#89ddff"> ===</span><span style="color:#89ddff"> undefined</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false (different types)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> object </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {}</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(object </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> {}</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false (because objects are compared by reference)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(object </span><span style="color:#89ddff">===</span><span style="color:#babed8"> object)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true (because the object is referentially equal to itself)</span></span></code></pre><h3 id="loose" class="group relative"><a data-discover="true" href="#loose" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Loose permalink"/></a>Loose</h3><p>Loose equality is invoked using <code>==</code> and often produces unexpected results. For two values of the same type, <code>a</code> and <code>b</code>, <code>a == b</code> behaves like <code>a === b</code>. If <code>a</code> and <code>b</code> have different types, then JavaScript coerces the values to the same type and strictly equates them:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#f78c6c">1</span><span style="color:#89ddff"> ==</span><span style="color:#f78c6c"> 1</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#f78c6c">1</span><span style="color:#89ddff"> ==</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">1</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true (because the string was converted to a number)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">&#x27;</span><span style="color:#c3e88d">1</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff"> ==</span><span style="color:#f78c6c"> 1</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true (because the string was converted to a number)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#f78c6c">0</span><span style="color:#89ddff"> ==</span><span style="color:#ff9cac"> false</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true (because the boolean was converted to a number)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#f78c6c">0</span><span style="color:#89ddff"> ==</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false (because absent values are not considered equal to non-absent values)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">{}</span><span style="color:#89ddff"> ==</span><span style="color:#89ddff"> {}</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false (because objects are compared by reference)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#f78c6c">0</span><span style="color:#89ddff"> ==</span><span style="color:#89ddff"> undefined</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false (because absent values are not considered equal to non-absent values)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">null</span><span style="color:#89ddff"> ==</span><span style="color:#89ddff"> undefined</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true (because both are absent values)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">undefined</span><span style="color:#89ddff"> ==</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true (because both are absent values)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">&#x27;</span><span style="color:#c3e88d">hello!</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff"> ==</span><span style="color:#ff9cac"> false</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">&#x27;&#x27;</span><span style="color:#89ddff"> ==</span><span style="color:#ff9cac"> false</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true (because the string was converted to a boolean and an empty string sort of represents false in the realm of strings I guess)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">([] </span><span style="color:#89ddff">==</span><span style="color:#ff9cac"> false</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">// true (because the array was converted to a boolean and an empty array sort of represents false in the realm of arrays I guess)</span></span></code></pre><p>If you’re feeling confused, then you wouldn’t be the only one. This <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness#Loose_equality_using" rel="nofollow">operand conversion table</a> and <a href="https://www.sitepoint.com/javascript-truthy-falsy" rel="nofollow">article about “truthy” and “falsy” values</a> explain loose equality more fully. For a handy reference on the behavior of <code>==</code> and <code>===</code>, look no further than this <a href="https://dorey.github.io/JavaScript-Equality-Table/unified" rel="nofollow">JavaScript equality table</a>.</p><h2 id="the-right-way-to-check-for-an-absent-value" class="group relative"><a data-discover="true" href="#the-right-way-to-check-for-an-absent-value" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="The right way to check for an absent value permalink"/></a>The right way to check for an absent value</h2><p>Now we can check which expressions from the beginning of the post work! Let’s take a look at the first expression:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(value </span><span style="color:#89ddff">==</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span></code></pre><ul><li><p><em>Does it evaluate to <code>true</code> for <code>undefined</code>?</em> Yes, because <code>undefined</code> and <code>null</code> are loosely equal.</p></li><li><p><em>Does it evaluate to <code>true</code> for <code>null</code>?</em> Yes, because <code>null</code> is equal to itself.</p></li><li><p><em>Does it evaluate to <code>false</code> for everything else?</em> Yes, because <code>null</code> is only loosely equal to itself and <code>undefined</code>.</p></li></ul><p><code>value == undefined</code> would also work for roughly the same reasons, but <code>value == null</code> is safer because <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined#:~:text=Note%3A%20While%20you%20can%20use%20undefined%20as%20an%20identifier%20(variable%20name)%20in%20any%20scope%20other%20than%20the%20global%20scope%20(because%20undefined%20is%20not%20a%20reserved%20word)%2C%20doing%20so%20is%20a%20very%20bad%20idea%20that%20will%20make%20your%20code%20difficult%20to%20maintain%20and%20debug." rel="nofollow"><code>undefined</code> could be shadowed</a> or reassigned in pre-ES5 JavaScript environments. This can’t happen with <code>null</code> because it is a literal.</p><h3 id="undeclared-variables" class="group relative"><a data-discover="true" href="#undeclared-variables" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Undeclared variables permalink"/></a>Undeclared variables</h3><p>These methods work except for one lurking issue. If <code>value</code> is undeclared, then our code would throw a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError" rel="nofollow"><code>ReferenceError</code></a>.</p><p>That may sound like a nonissue, but consider that some JavaScript needs to be compatible with both the browser and Node.js, and that the two environments differ in which global variables are declared. For example, in the browser the global variable <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window" rel="nofollow"><code>window</code></a> is declared, but there’s no such variable in Node.js. Can we access the <code>window</code> variable only if it exists and avoid a <code>ReferenceError</code>?</p><p>It turns out that <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof#interaction_with_undeclared_and_uninitialized_variables" rel="nofollow">the <code>typeof</code> operator returns <code>&#x27;undefined&#x27;</code> for an undeclared variable instead of throwing a <code>ReferenceError</code></a>. This is convenient because <code>typeof undefined</code> also returns <code>&#x27;undefined&#x27;</code> so <code>typeof value === &#x27;undefined&#x27;</code> checks for both undeclared variables and <code>undefined</code>. To check for <code>null</code> as well we can add an additional check using <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Logical_OR" rel="nofollow">logical “or”</a>.</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">typeof</span><span style="color:#babed8"> value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">undefined</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff"> ||</span><span style="color:#babed8"> value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span></code></pre><ul><li><p><em>Does it evaluate to <code>true</code> when <code>value</code> is undeclared?</em> Yes, because the <code>typeof</code> operator returns <code>&#x27;undefined&#x27;</code> for an undeclared variable.</p></li><li><p><em>Does it evaluate to <code>true</code> for <code>undefined</code>?</em> Yes, because <code>typeof undefined</code> returns <code>&#x27;undefined&#x27;</code>.</p></li><li><p><em>Does it evaluate to <code>true</code> for <code>null</code>?</em> Yes, the first condition evaluates to <code>false</code>, but the second condition evaluates to <code>true</code> because <code>null</code> is equal to itself.</p></li><li><p><em>Does it evaluate to <code>false</code> for everything else?</em> Yes, the <code>typeof</code> operator only returns <code>&#x27;undefined&#x27;</code> for undeclared variables and <code>undefined</code>, and <code>null</code> is only strictly equal to itself.</p></li></ul><p>This method works in every situation, but it is only preferable over <code>value == null</code> when you don’t know if <code>value</code> has been declared<sup><a href="#fn-2" aria-describedby="footnote-label" data-footnote-ref="" id="fnref-2">2</a></sup>.</p><h3 id="the-problems-with-the-other-expressions" class="group relative"><a data-discover="true" href="#the-problems-with-the-other-expressions" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="The problems with the other expressions permalink"/></a>The problems with the other expressions</h3><p>A few of the expressions at the beginning of the post look almost identical to the expression we just evaluated. In fact, the following expressions are equivalent to <code>typeof value === &#x27;undefined&#x27; || value === null</code>:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">typeof</span><span style="color:#babed8"> value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">undefined</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff"> ||</span><span style="color:#babed8"> value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">typeof</span><span style="color:#babed8"> value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">undefined</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff"> ||</span><span style="color:#babed8"> value </span><span style="color:#89ddff">==</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">typeof</span><span style="color:#babed8"> value </span><span style="color:#89ddff">==</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">undefined</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff"> ||</span><span style="color:#babed8"> value </span><span style="color:#89ddff">==</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">typeof</span><span style="color:#babed8"> value </span><span style="color:#89ddff">==</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">undefined</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff"> ||</span><span style="color:#babed8"> value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span></code></pre><p>So why choose the expression that uses strict equality for both conditions? I prefer to avoid loose equality because it’s confusing and in this case it’s not required for correct behavior.</p><p>Let’s evaluate the rest of the expressions from the beginning of the post:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#676e95;font-style:italic">// Doesn&#x27;t account for undefined</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676e95;font-style:italic">// Doesn&#x27;t account for null</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> undefined</span><span style="color:#babed8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676e95;font-style:italic">// Works, but is much more verbose than value == null</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> undefined</span><span style="color:#89ddff"> ||</span><span style="color:#babed8"> value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676e95;font-style:italic">// Doesn&#x27;t account for null</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">typeof</span><span style="color:#babed8"> value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">undefined</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676e95;font-style:italic">// Doesn&#x27;t account for null</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">typeof</span><span style="color:#babed8"> value </span><span style="color:#89ddff">==</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">undefined</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#676e95;font-style:italic">// Erroneously evaluates to true for falsy values such as false, &#x27;&#x27;, [], and 0</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">!</span><span style="color:#babed8">value)</span></span></code></pre><h2 id="absent-object-properties" class="group relative"><a data-discover="true" href="#absent-object-properties" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Absent object properties permalink"/></a>Absent object properties</h2><p>An object property can be set to an absent value, but the property itself can also be absent:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> object1 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {}</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> object2 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {</span><span style="color:#f07178"> property</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> undefined</span><span style="color:#89ddff"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(object1</span><span style="color:#89ddff">.</span><span style="color:#babed8">property </span><span style="color:#89ddff">==</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(object2</span><span style="color:#89ddff">.</span><span style="color:#babed8">property </span><span style="color:#89ddff">==</span><span style="color:#89ddff"> null</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span></code></pre><p>The result for the two objects is the same because the access of an absent property returns <code>undefined</code>. This makes <code>value == null</code> a good solution when checking for <code>null</code>, <code>undefined</code>, <em>and</em> absent properties. However, specifically checking for an absent property requires a different method.</p><p>One way is to use the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in" rel="nofollow"><code>in</code></a> operator:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> object1 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {}</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> object2 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {</span><span style="color:#f07178"> property</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> undefined</span><span style="color:#89ddff"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">&#x27;</span><span style="color:#c3e88d">property</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff"> in</span><span style="color:#babed8"> object1)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">&#x27;</span><span style="color:#c3e88d">property</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff"> in</span><span style="color:#babed8"> object2)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span></code></pre><p>Note that the left-hand side of the <code>in</code> operator must be a <code>string</code> or <code>Symbol</code>, not an identifier. This may seem like a good solution, but consider the following case:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> object1 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {}</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> object2 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {</span><span style="color:#f07178"> constructor</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> undefined</span><span style="color:#89ddff"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">&#x27;</span><span style="color:#c3e88d">constructor</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff"> in</span><span style="color:#babed8"> object1)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">&#x27;</span><span style="color:#c3e88d">constructor</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff"> in</span><span style="color:#babed8"> object2)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span></code></pre><p>Probably not what you expected right? The expression <code>&#x27;constructor&#x27; in object1</code> returns <code>true</code> because the <code>constructor</code> property was inherited from the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype" rel="nofollow">object’s prototype chain</a>. The <code>in</code> operator considers both the specific properties of the object as well as its inherited properties.</p><p>This a nonissue when the object has a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object#null-prototype_objects" rel="nofollow"><code>null</code> prototype</a> because there are no inherited properties:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> object </span><span style="color:#89ddff">=</span><span style="color:#babed8"> Object</span><span style="color:#89ddff">.</span><span style="color:#82aaff">create</span><span style="color:#babed8">(</span><span style="color:#89ddff">null</span><span style="color:#babed8">)</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#89ddff">`</span><span style="color:#c3e88d">constructor</span><span style="color:#89ddff">`</span><span style="color:#89ddff"> in</span><span style="color:#babed8"> object)</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false</span></span></code></pre><p>But most of the time the object doesn’t have a <code>null</code> prototype or we don’t know if it does. A more robust solution is to only check the uninherited properties using the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty" rel="nofollow"><code>hasOwnProperty</code></a> method, which is inherited from <code>Object</code>:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> object1 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {}</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> object2 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {</span><span style="color:#f07178"> constructor</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> undefined</span><span style="color:#89ddff"> }</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(object1</span><span style="color:#89ddff">.</span><span style="color:#82aaff">hasOwnProperty</span><span style="color:#babed8">(</span><span style="color:#89ddff">&#x27;</span><span style="color:#c3e88d">constructor</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(object2</span><span style="color:#89ddff">.</span><span style="color:#82aaff">hasOwnProperty</span><span style="color:#babed8">(</span><span style="color:#89ddff">&#x27;</span><span style="color:#c3e88d">constructor</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span></code></pre><p>There are a couple of pitfalls to using the <code>hasOwnProperty</code> method:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> object1 </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {</span><span style="color:#82aaff"> hasOwnProperty</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> ()</span><span style="color:#c792ea"> =&gt;</span><span style="color:#ff9cac"> true</span><span style="color:#89ddff"> }</span></span>
<span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> object2 </span><span style="color:#89ddff">=</span><span style="color:#babed8"> Object</span><span style="color:#89ddff">.</span><span style="color:#82aaff">create</span><span style="color:#babed8">(</span><span style="color:#89ddff">null</span><span style="color:#babed8">)</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(object1</span><span style="color:#89ddff">.</span><span style="color:#82aaff">hasOwnProperty</span><span style="color:#babed8">(</span><span style="color:#89ddff">&#x27;</span><span style="color:#c3e88d">property</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; true</span></span>
<span class="line"></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(object2</span><span style="color:#89ddff">.</span><span style="color:#82aaff">hasOwnProperty</span><span style="color:#babed8">(</span><span style="color:#89ddff">&#x27;</span><span style="color:#c3e88d">property</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; TypeError: object2.hasOwnProperty is not a function</span></span></code></pre><p><code>object1</code>’s <code>hasOwnProperty</code> method was shadowed by a method that always returns <code>true</code>. <code>object2</code> was created with a <code>null</code> prototype so it does not inherit <code>hasOwnProperty</code> from <code>Object</code>. There are two ways around these pitfalls:</p><ul><li><p>Access <code>Object</code>’s <code>hasOwnProperty</code> method directly:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> object </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {</span><span style="color:#82aaff"> hasOwnProperty</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> ()</span><span style="color:#c792ea"> =&gt;</span><span style="color:#ff9cac"> true</span><span style="color:#89ddff"> }</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(</span><span style="color:#ffcb6b">Object</span><span style="color:#89ddff">.</span><span style="color:#babed8">prototype</span><span style="color:#89ddff">.</span><span style="color:#babed8">hasOwnProperty</span><span style="color:#89ddff">.</span><span style="color:#82aaff">call</span><span style="color:#babed8">(object</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">property</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false</span></span></code></pre></li><li><p>Use <code>Object</code>’s <em>static</em> <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn" rel="nofollow"><code>hasOwn</code></a> method:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#c792ea">const</span><span style="color:#babed8"> object </span><span style="color:#89ddff">=</span><span style="color:#89ddff"> {</span><span style="color:#82aaff"> hasOwnProperty</span><span style="color:#89ddff">:</span><span style="color:#89ddff"> ()</span><span style="color:#c792ea"> =&gt;</span><span style="color:#ff9cac"> true</span><span style="color:#89ddff"> }</span></span>
<span class="line"><span style="color:#babed8">console</span><span style="color:#89ddff">.</span><span style="color:#82aaff">log</span><span style="color:#babed8">(Object</span><span style="color:#89ddff">.</span><span style="color:#82aaff">hasOwn</span><span style="color:#babed8">(object</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">property</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">))</span></span>
<span class="line"><span style="color:#676e95;font-style:italic">//=&gt; false</span></span></code></pre><p><code>hasOwn</code> was added to JavaScript to avoid <code>hasOwnProperty</code>’s pitfalls, but at the time of writing it is <a href="https://caniuse.com/mdn-javascript_builtins_object_hasown" rel="nofollow">relatively new</a>.</p></li></ul><h2 id="recap" class="group relative"><a data-discover="true" href="#recap" class="absolute top-1/2 size-6 -translate-x-[1.85rem] -translate-y-1/2 opacity-0 group-hover:opacity-100 focus:opacity-100 in-[summary]:-translate-x-[3.35rem] focus-ring"><img src="data:image/svg+xml,%3csvg%20xmlns=&#x27;http://www.w3.org/2000/svg&#x27;%20fill=&#x27;none&#x27;%20viewBox=&#x27;0%200%2024%2024&#x27;%20stroke-width=&#x27;1.5&#x27;%20stroke=&#x27;currentColor&#x27;%3e%3cpath%20stroke-linecap=&#x27;round&#x27;%20stroke-linejoin=&#x27;round&#x27;%20d=&#x27;M13.19%208.688a4.5%204.5%200%20011.242%207.244l-4.5%204.5a4.5%204.5%200%2001-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5%204.5%200%2000-6.364-6.364l-4.5%204.5a4.5%204.5%200%20001.242%207.244&#x27;%20/%3e%3c/svg%3e" class="not-prose m-0 size-6" alt="Recap permalink"/></a>Recap</h2><p>Checking if <code>value</code> is set to an absent value:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#babed8">value </span><span style="color:#89ddff">==</span><span style="color:#89ddff"> null</span></span></code></pre><p>Checking if <code>value</code> is undeclared or set to an absent value:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#89ddff">typeof</span><span style="color:#babed8"> value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">undefined</span><span style="color:#89ddff">&#x27;</span><span style="color:#89ddff"> ||</span><span style="color:#babed8"> value </span><span style="color:#89ddff">===</span><span style="color:#89ddff"> null</span></span></code></pre><p>Checking if <code>&#x27;property&#x27;</code> in <code>object</code> is absent or set to an absent value:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#babed8">object</span><span style="color:#89ddff">.</span><span style="color:#babed8">property </span><span style="color:#89ddff">==</span><span style="color:#89ddff"> null</span></span></code></pre><p>Checking if <code>property</code> in <code>object</code> is absent:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#89ddff">!</span><span style="color:#ffcb6b">Object</span><span style="color:#89ddff">.</span><span style="color:#babed8">prototype</span><span style="color:#89ddff">.</span><span style="color:#babed8">hasOwnProperty</span><span style="color:#89ddff">.</span><span style="color:#82aaff">call</span><span style="color:#babed8">(object</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">property</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">)</span></span></code></pre><p>Checking if <code>property</code> in <code>object</code> is absent in <a href="https://caniuse.com/mdn-javascript_builtins_object_hasown" rel="nofollow">modern browsers</a>:</p><pre style="background-color:#292d3e;color:#babed8"><code><span class="line"><span style="color:#89ddff">!</span><span style="color:#babed8">Object</span><span style="color:#89ddff">.</span><span style="color:#82aaff">hasOwn</span><span style="color:#babed8">(object</span><span style="color:#89ddff">,</span><span style="color:#89ddff"> &#x27;</span><span style="color:#c3e88d">property</span><span style="color:#89ddff">&#x27;</span><span style="color:#babed8">)</span></span></code></pre><section class="footnotes" data-footnotes=""><h2 id="footnote-label" class="sr-only">Footnotes</h2><ol><li id="fn-1"><p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void#examples" rel="nofollow">MDN has some examples</a> of when the <code>void</code> operator is useful. <a href="#fnref-1" aria-label="Back to reference 1" class="data-footnote-backref">↩</a></p></li><li id="fn-2"><p>CoffeeScript follows the same principle when transpiling its <a href="https://coffeescript.org/#existential-operator" rel="nofollow">existential operator</a> to JavaScript. <a href="#fnref-2" aria-label="Back to reference 2" class="data-footnote-backref">↩</a></p></li></ol></section>]]></content:encoded>
                <description><![CDATA[JavaScript has a lot of similar-looking ways to check for the absence of a value: Which one is right? Absent values JavaScript has two representations of an absent value. Undefined undefined is a…]]></description>
                <guid isPermaLink="true">https://tomeraberba.ch/checking-for-the-absence-of-a-value-in-javascript</guid>
                <pubDate>Thu, 16 Aug 2018 00:00:00 GMT</pubDate>
                <lastBuildDate>Wed, 11 Jan 2023 00:00:00 GMT</lastBuildDate>
              </item>
            
      </channel>
    </rss>
  