<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>NotesToSelf.Dev</title><link>https://notestoself.dev/</link><description>Recent content on NotesToSelf.Dev</description><generator>Hugo</generator><language>en-gb</language><lastBuildDate>Mon, 15 Jun 2026 21:13:34 +0100</lastBuildDate><atom:link href="https://notestoself.dev/index.xml" rel="self" type="application/rss+xml"/><item><title>Overlap-preserving type refinement assertion signatures in TypeScript</title><link>https://notestoself.dev/posts/typescript-overlap-preserving-type-refinement-assertion-signatures/</link><pubDate>Mon, 15 Jun 2026 17:16:03 +0000</pubDate><guid>https://notestoself.dev/posts/typescript-overlap-preserving-type-refinement-assertion-signatures/</guid><description>&lt;p&gt;In the last post I wrote about how TypeScript assertion signatures are more useful when they
can &lt;a href="https://notestoself.dev/posts/combine-typescript-assertion-signatures-function-overloads/" title="Combining TypeScript assertion signatures and function overloads"&gt;incorporate existing type information&lt;/a&gt;
from the calling scope.&lt;/p&gt;
&lt;p&gt;Sometimes the asserted type is less precise than information TypeScript already has in scope at the
calling site.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;assertString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;asserts&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Expected string&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;draft&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;published&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;archived&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getFoo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Foo&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;draft&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getFoo&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;assertString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// const foo: &amp;#34;draft&amp;#34; | &amp;#34;published&amp;#34; | &amp;#34;archived&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The assertion signature from &lt;code&gt;assertString&lt;/code&gt; tells TypeScript that &lt;code&gt;foo&lt;/code&gt; is a string, and TypeScript
is able to combine that with other type information from the scope to infer that &lt;code&gt;foo&lt;/code&gt; must
therefore be of a literal union type &lt;code&gt;&amp;quot;draft&amp;quot; | &amp;quot;published&amp;quot; | &amp;quot;archived&amp;quot;&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Combining TypeScript assertion signatures and function overloads</title><link>https://notestoself.dev/posts/combine-typescript-assertion-signatures-function-overloads/</link><pubDate>Mon, 15 Jun 2026 10:46:07 +0000</pubDate><guid>https://notestoself.dev/posts/combine-typescript-assertion-signatures-function-overloads/</guid><description>&lt;p&gt;TypeScript has a useful feature
called &lt;a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#assertion-functions"&gt;assertion signatures&lt;/a&gt;
that allows you to write functions that can be used to narrow the type of a value. For example, you
can write a function that asserts that a value is a string and tells TypeScript that after this
function has been called, that value must be a string.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;assertIsString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;asserts&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;string&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Expected string&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is somewhat similar to type guards, but rather than being used in a condition, it lets
TypeScript narrow the type in the current scope without introducing a new variable.&lt;/p&gt;</description></item><item><title>Composable type narrowing test assertion matchers in TypeScript</title><link>https://notestoself.dev/posts/composable-type-narrow-test-assertion-matchers-typescript/</link><pubDate>Sun, 14 Jun 2026 13:23:05 +0000</pubDate><guid>https://notestoself.dev/posts/composable-type-narrow-test-assertion-matchers-typescript/</guid><description>&lt;p&gt;A lot of tests use the &lt;code&gt;expect().toMatchObject()&lt;/code&gt; assertion in Jest or Vitest, because we&amp;rsquo;re most
often dealing with object structures returned from the subject of the test. The &lt;code&gt;toMatchObject&lt;/code&gt;
assertion is useful because it applies a deep partial match, so you only need to specify the
particular property paths you&amp;rsquo;re interested in. It doesn&amp;rsquo;t matter if other properties are added to
the object structure later.&lt;/p&gt;
&lt;p&gt;However, I would often find the need to add explicit type information in addition to the assertion,
because the fluent interface in &lt;code&gt;expect()&lt;/code&gt; cannot indicate any type information to TypeScript or the
IDE.&lt;/p&gt;</description></item><item><title>Preventing type errors in TypeScript test files with tsc -p --noEmit</title><link>https://notestoself.dev/posts/typescript-tsc-build-noemit-test-files/</link><pubDate>Sat, 13 Jun 2026 08:23:51 +0000</pubDate><guid>https://notestoself.dev/posts/typescript-tsc-build-noemit-test-files/</guid><description>&lt;p&gt;In TypeScript projects it&amp;rsquo;s common to have separate tsconfig files covering the entire project and
separate build contexts which exclude test files.&lt;/p&gt;
&lt;p&gt;This can lead to failing to check test files with tsc, so type errors in test files creep in
unnoticed only to be discovered later when the root problem is less clear.&lt;/p&gt;
&lt;p&gt;For example the main tsconfig.json file in a project might be similar to this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;compilerOptions&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;target&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ES2023&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;module&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;NodeNext&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;moduleResolution&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;NodeNext&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;lib&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ES2023&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;types&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;node&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;strict&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;outDir&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;dist&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;rootDir&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;src&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;include&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;src/**/*&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;test/**/*&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;typings/**/*.d.ts&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And then there might be a tsconfig.build.json file that extends it but excludes test files from the
build:&lt;/p&gt;</description></item><item><title>Simulating an S3 static website on localhost with Yulin</title><link>https://notestoself.dev/posts/yulin-simulate-s3-website-localhost/</link><pubDate>Sun, 07 Jun 2026 08:42:28 +0000</pubDate><guid>https://notestoself.dev/posts/yulin-simulate-s3-website-localhost/</guid><description>&lt;p&gt;If you&amp;rsquo;re using S3 to host a static website and want to test and develop it locally, you can use the
&lt;a href="https://github.com/KensioSoftware/yulin" title="TypeScript AWS simulator"&gt;@kensio/yulin&lt;/a&gt; package.&lt;/p&gt;
&lt;p&gt;This might seem pointless when it&amp;rsquo;s easy to serve a static website on localhost in various other
ways (such as with &lt;code&gt;hugo serve&lt;/code&gt;), but even for this small use-case Yulin does provide some extra
benefits.&lt;/p&gt;
&lt;p&gt;One immediate small benefit is that Yulin simulates behaviours of S3 static website serving such as
error documents, bucket redirects and routing rules with conditions. These might be important
behaviours for your project, so it&amp;rsquo;s useful to be able to develop locally with simulated
equivalents.&lt;/p&gt;</description></item><item><title>AWS CloudFront Functions JS2 tooling: ESLint, Vitest, IDE</title><link>https://notestoself.dev/posts/aws-cloudfront-function-js2-tooling-eslint-vitest-ide/</link><pubDate>Sun, 24 May 2026 14:58:56 +0000</pubDate><guid>https://notestoself.dev/posts/aws-cloudfront-function-js2-tooling-eslint-vitest-ide/</guid><description>&lt;p&gt;AWS CloudFront Functions (CFF) use a special subset of JavaScript called
&lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-20.html"&gt;JS2&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;JS2 syntax is simpler than full JavaScript, but it&amp;rsquo;s still nice to have modern tooling around it,
such as linting, testing, and IDE autocomplete. This tooling can also help avoid mistakes related to
the minimal JS2 feature set.&lt;/p&gt;
&lt;p&gt;Firstly, these docblock comments in the CFF JS2 file allow the IDE to understand more about the
function and offer more useful autocompletion and problem highlighting:&lt;/p&gt;</description></item><item><title>Fanalysis</title><link>https://notestoself.dev/work/fanalysis/</link><pubDate>Sun, 01 Jun 2025 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/fanalysis/</guid><description>&lt;p&gt;In June 2025 I was the second employee to join Fanalysis, as Staff Software
Engineer.&lt;/p&gt;</description></item><item><title>Bash script to run CDK synth only if there are changed TS files</title><link>https://notestoself.dev/posts/cdk-synth-on-change-bash-script/</link><pubDate>Tue, 08 Apr 2025 10:57:23 +0100</pubDate><guid>https://notestoself.dev/posts/cdk-synth-on-change-bash-script/</guid><description>&lt;p&gt;The &lt;a href="https://docs.aws.amazon.com/cdk/v2/guide/ref-cli-cmd-synth.html"&gt;cdk synth&lt;/a&gt;
command can be pretty slow, even when running for a single small stack. We also
often want to run the CDK synth command regularly as part of local development
setups, so it&amp;rsquo;s nice to skip the re-synth if no relevant files have changed.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a bash script that checks for changed TypeScript source files by
comparing their timestamp to that of the generated template file from the
previous synth. This way we only run CDK synth when we find a relevant change.&lt;/p&gt;</description></item><item><title>AWS SAM local (WSGI) drops headers with underscores</title><link>https://notestoself.dev/posts/sam-local-wsgi-drops-headers-underscore/</link><pubDate>Sun, 30 Mar 2025 11:57:23 +0100</pubDate><guid>https://notestoself.dev/posts/sam-local-wsgi-drops-headers-underscore/</guid><description>&lt;p&gt;&lt;strong&gt;TLDR: SAM local drops headers that contain an underscore, due to using Flask
and WSGI internally, which differs from the behaviour of a deployed API
Gateway.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m working on an existing project that uses AWS CDK to provision API Gateway
in front of some Lambda functions. The team would like to be able to run this
locally for faster development iterations, so I set that up with the &lt;code&gt;sam local start-api&lt;/code&gt; command, using the template emitted by &lt;code&gt;cdk synth&lt;/code&gt;. So far, so good.&lt;/p&gt;</description></item><item><title>Cache a public Docker image in a GitHub Action</title><link>https://notestoself.dev/posts/github-action-cache-public-docker-image/</link><pubDate>Sun, 23 Mar 2025 11:57:23 +0100</pubDate><guid>https://notestoself.dev/posts/github-action-cache-public-docker-image/</guid><description>&lt;p&gt;Here&amp;rsquo;s a reusable GitHub Action definition that caches a public Docker image:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Docker public image cache&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Pull and cache a public Docker image for reuse&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image_name_tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Docker image name and tag to pull and cache&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;using&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;composite&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Restore image cache&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;restore_cache&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/cache@v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ci/cache/docker&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;cache-docker-${{ inputs.image_name_tag }}&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Update image cache on cache miss&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;steps.restore_cache.outputs.cache-hit != &amp;#39;true&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;bash&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; docker pull &amp;#39;${{ inputs.image_name_tag }}&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;amp;&amp;amp; mkdir -p $(dirname &amp;#39;./ci/cache/docker/${{ inputs.image_name_tag }}.tar&amp;#39;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;amp;&amp;amp; docker image save &amp;#39;${{ inputs.image_name_tag }}&amp;#39; --output &amp;#39;./ci/cache/docker/${{ inputs.image_name_tag }}.tar&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Use image cache on cache hit&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;steps.restore_cache.outputs.cache-hit == &amp;#39;true&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;bash&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; docker image load --input &amp;#39;./ci/cache/docker/${{ inputs.image_name_tag }}.tar&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you have this action definition at &lt;code&gt;.github/actions/docker_image_cache&lt;/code&gt;
in the repo, then you can use it from a workflow like this:&lt;/p&gt;</description></item><item><title>eslint changed TypeScript files only</title><link>https://notestoself.dev/posts/eslint-changed-ts-files-only/</link><pubDate>Sun, 23 Mar 2025 10:57:23 +0100</pubDate><guid>https://notestoself.dev/posts/eslint-changed-ts-files-only/</guid><description>&lt;p&gt;It&amp;rsquo;s best if a linter is applied from the beginning
of a project, and with default linting rules, as that way the project
&lt;a href="https://notestoself.dev/posts/always-lint/"&gt;benefits from the linting&lt;/a&gt;
with the minimum possible effort.&lt;/p&gt;
&lt;p&gt;However, we often join existing projects which have already been developed
without the use of a linter, by which point there are possibly thousands of
linter violations. In that situation, one option is to incrementally improve
the linting by only applying the linter to changed files. New code from then
on is compliant, and it&amp;rsquo;s less of a burden to sort out existing violations in
existing files that developers touch as part of a change.&lt;/p&gt;</description></item><item><title>Splitting a text file into multiple separate files by section with csplit</title><link>https://notestoself.dev/posts/bash-split-text-file-multiple-separate-sections/</link><pubDate>Wed, 26 Feb 2025 10:57:23 +0100</pubDate><guid>https://notestoself.dev/posts/bash-split-text-file-multiple-separate-sections/</guid><description>&lt;p&gt;Here&amp;rsquo;s a bash command to split a text file into multiple separate files by
section delimited by a regex pattern matching titled sections in the source
file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;csplit &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -z &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -f section_ &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -b &lt;span class="s2"&gt;&amp;#34;%02d.txt&amp;#34;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; document.txt &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;/\nSection [0-9]\+\n/&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;{*}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The key part is the &lt;code&gt;/\nSection [0-9]\+\n/&lt;/code&gt; regex pattern, which matches section
titles such as &amp;ldquo;Section 18&amp;rdquo; alone on a line.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an explanation of the the rest of the command:&lt;/p&gt;</description></item><item><title>Python "raises in exception group" context manager for tests</title><link>https://notestoself.dev/posts/python-tests-raises-exception-group-matching-context-manager/</link><pubDate>Sun, 05 Jan 2025 17:11:23 +0100</pubDate><guid>https://notestoself.dev/posts/python-tests-raises-exception-group-matching-context-manager/</guid><description>&lt;p&gt;Here&amp;rsquo;s a Python testing utility function for asserting on an expected
&lt;code&gt;Exception&lt;/code&gt; type from within an
&lt;a href="https://peps.python.org/pep-0654/"&gt;ExceptionGroup&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;contextlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;contextmanager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@contextmanager&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;raises_in_group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;expected_exception&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ne"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;match_message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Like pytest.raises, but for an Exception inside an ExceptionGroup, for
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; example coming from asyncio.TaskGroup.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;matched_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;matched_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;yield&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;expected_exception&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ExceptionGroup&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;matched_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exceptions&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;expected_exception&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;matched_message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;exp&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;matched_type&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;match_message&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matched_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Expected at least one &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;expected_exception&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; exception.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;matched_message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Expected at least one &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;expected_exception&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; exception matching &amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;match_message&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39;.&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It can be used in test cases in a similar way to
&lt;a href="https://docs.pytest.org/en/7.1.x/reference/reference.html#pytest.raises"&gt;pytest.raises&lt;/a&gt;,
like this:&lt;/p&gt;</description></item><item><title>HOA Mailers</title><link>https://notestoself.dev/work/hoamailers/</link><pubDate>Wed, 01 Jan 2025 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/hoamailers/</guid><description>&lt;p&gt;I helped HOA Mailers with their AWS infrastructure, backend systems and frontend
software.&lt;/p&gt;</description></item><item><title>Creating AWS Organizational Units and Accounts for Prod and Dev in CloudFormation</title><link>https://notestoself.dev/posts/aws-cloudformation-create-organizational-units-accounts-prod-dev/</link><pubDate>Sat, 05 Oct 2024 15:44:45 +0100</pubDate><guid>https://notestoself.dev/posts/aws-cloudformation-create-organizational-units-accounts-prod-dev/</guid><description>&lt;p&gt;Here&amp;rsquo;s an AWS CloudFormation template and deployment script to set up
&lt;a href="https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_ous.html"&gt;Organizational Units&lt;/a&gt;
and
&lt;a href="https://docs.aws.amazon.com/organizations/"&gt;Accounts&lt;/a&gt;
for Prod and Dev for a new project or department.&lt;/p&gt;
&lt;p&gt;Deploying a CloudFormation stack from this template will require a user in the
Organization&amp;rsquo;s
&lt;a href="https://docs.aws.amazon.com/organizations/latest/userguide/orgs-manage_accounts_management.html"&gt;management account&lt;/a&gt;
with the &lt;code&gt;AdministratorAccess&lt;/code&gt; permission set.&lt;/p&gt;
&lt;p&gt;The created Organizational Unit (OU) and Account structure will look this:&lt;/p&gt;
&lt;pre class="text-diagram"&gt;
Organization Root [pre-existing]

 ╚═ Management Account [pre-existing]

 ╚═ Project Parent OU

 ╚═ Project Prod OU

 ╚═ Project Prod Account

 ╚═ Project Dev OU

 ╚═ Project Dev Account
&lt;/pre&gt;


&lt;p&gt;The OUs are not strictly necessary, but they help to group the whole project
under a single OU, and to allow for other accounts to be added later within the
Prod and Dev OUs if necessary.&lt;/p&gt;</description></item><item><title>Reduce pragma no cover comments with tool.coverage.report exclude_also</title><link>https://notestoself.dev/posts/py-coverage-pragma-no-cover-pyproject-tool-coverage-report-exclude-also/</link><pubDate>Sun, 29 Sep 2024 19:45:13 +0100</pubDate><guid>https://notestoself.dev/posts/py-coverage-pragma-no-cover-pyproject-tool-coverage-report-exclude-also/</guid><description>&lt;p&gt;If you&amp;rsquo;re using &lt;a href="https://coverage.readthedocs.io/"&gt;Coverage.py&lt;/a&gt; or
&lt;a href="https://pypi.org/project/pytest-cov/"&gt;pytest-cov&lt;/a&gt; in your Python project, you
probably end up with &lt;code&gt;# pragma: no cover&lt;/code&gt; comments all over the place to prevent
odd lines from being marked as missing coverage, like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Foobar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;do_something&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="c1"&gt;# pragma: no cover&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;NotImplementedError&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can reduce the need for these &lt;code&gt;# pragma: no cover&lt;/code&gt; comments by configuring
&lt;code&gt;Coverage.py&lt;/code&gt; to ignore certain patterns by default. This can be configured in
the &lt;code&gt;pyproject.toml&lt;/code&gt; file like this:&lt;/p&gt;</description></item><item><title>Github Action to post a Telegram notification with no dependencies</title><link>https://notestoself.dev/posts/github-action-telegram-notification-no-dependencies/</link><pubDate>Sun, 29 Sep 2024 19:32:04 +0100</pubDate><guid>https://notestoself.dev/posts/github-action-telegram-notification-no-dependencies/</guid><description>&lt;p&gt;Here&amp;rsquo;s a Github Action that posts a notification message to a Telegram chat,
without using any dependencies.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Post to Telegram&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Post a message to a Telegram channel&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;telegram_bot_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Auth token of the Telegram bot via which to post.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;telegram_chat_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ID of the Telegram channel to which to send the message.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;message_content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;String content of the Telegram message to send.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;using&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;composite&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;bash&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; curl &amp;#34;https://api.telegram.org/bot${{ inputs.telegram_bot_token }}/sendMessage&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; --header &amp;#34;Content-Type: application/json&amp;#34; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; --request POST \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; --data &amp;#39;{
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;chat_id&amp;#34;: &amp;#34;${{ inputs.telegram_chat_id }}&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; &amp;#34;text&amp;#34;: &amp;#34;${{ inputs.message_content }}&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; }&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The key part of the action is the bash script that uses curl to post a message
to Telegram:&lt;/p&gt;</description></item><item><title>Python Coverage boolean compound condition line exit missing branch coverage</title><link>https://notestoself.dev/posts/py-coverage-compound-boolean-line-exit/</link><pubDate>Sat, 28 Sep 2024 13:11:39 +0100</pubDate><guid>https://notestoself.dev/posts/py-coverage-compound-boolean-line-exit/</guid><description>&lt;p&gt;With &lt;a href="https://coverage.readthedocs.io/en/latest/branch.html"&gt;branch coverage&lt;/a&gt;
enabled in &lt;code&gt;Coverage.py&lt;/code&gt; or &lt;code&gt;pytest-coverage&lt;/code&gt;, you might see it report missing
coverage on a compound boolean condition, like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foobar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;condition_1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;condition_2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;condition_3&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;condition_4&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;condition_5&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That might lead to a report that the line where the boolean compound condition
begins to the exit of the function are missing coverage. The report will look
like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---------- coverage: platform linux, python 3.12.1-final-0 -------
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Name Stmts Miss Branch BrPart Cover Missing
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;src/foobar.py 39 0 16 1 98% 37-&amp;gt;exit
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;------------------------------------------------------------------
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;TOTAL 188 0 68 1 99%
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &amp;ldquo;line to exit&amp;rdquo; part is &lt;code&gt;37-&amp;gt;exit&lt;/code&gt; in the &lt;code&gt;Missing&lt;/code&gt; column on the right.&lt;/p&gt;</description></item><item><title>Python recursive override path function and mixin class</title><link>https://notestoself.dev/posts/python-recursive-override-paths-function-mixin/</link><pubDate>Wed, 25 Sep 2024 13:36:56 +0100</pubDate><guid>https://notestoself.dev/posts/python-recursive-override-paths-function-mixin/</guid><description>&lt;p&gt;Here&amp;rsquo;s a recursive utility function in Python that allows overriding nested
paths through objects that can be mutable mappings, mutable sequences or
dataclass instances.&lt;/p&gt;
&lt;p&gt;The function allows doing things like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;override_paths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;foo0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;bar&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;lvl1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;foo1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;bar1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;list1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;foobar1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Foobar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hello1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;foo0&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;123&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;lvl1__foo1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;456&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;lvl1__list1__3&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;lvl1__foobar1__foo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hello2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;foo0&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;123&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;lvl1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;foo1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;456&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;list1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;foobar1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Foobar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;hello2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There is also a &lt;code&gt;OverridableMixin&lt;/code&gt; that makes it convenient to do things like
this inline:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;Foobar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;overridden&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo__bar__baz&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;dataclasses&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;copy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;deepcopy&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;dataclasses&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;is_dataclass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;itertools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;TYPE_CHECKING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;ClassVar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;MutableMapping&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;MutableSequence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;TypeVar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Dataclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;Identifies an instance of any dataclass.&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;__dataclass_fields__&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ClassVar&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;TYPE_CHECKING&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Overridable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MutableMapping&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;MutableSequence&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Dataclass&lt;/span&gt; &lt;span class="c1"&gt;# pragma: no cover&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Having to add `| object` to the end of this union to keep PyCharm happy as&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# the PyCharm type checker can&amp;#39;t figure out the Dataclass Protocol above.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Doing this inside a TYPE_CHECKING condition so that mypy still validates&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# the protocol correctly.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Overridable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MutableMapping&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;MutableSequence&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Dataclass&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Using TypeVar instead of the inline type parameter syntax because pydocstyle&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# seems unable to cope with the newer syntax.&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TypeVar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;T&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bound&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;Overridable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;override_paths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Override keys, indexes or fields specified via a flat path syntax.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; E.g.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; override_paths({&amp;#34;foo&amp;#34;: {&amp;#34;bar&amp;#34;: 123}}, foo__bar=456)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; {&amp;#34;foo&amp;#34;: {&amp;#34;bar&amp;#34;: 456}}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; The override paths can be given as either kwargs, or as args as tuples with
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; the path and the value as pairs. The path can be a __ string or a list of
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; strings.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; override_paths({&amp;#34;foo&amp;#34;: {&amp;#34;bar&amp;#34;: 123}}, (&amp;#34;foo__bar&amp;#34;, 456))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; {&amp;#34;foo&amp;#34;: {&amp;#34;bar&amp;#34;: 456}}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; override_paths({&amp;#34;foo&amp;#34;: {&amp;#34;bar&amp;#34;: 123}}, ([&amp;#34;foo, &amp;#34;bar&amp;#34;], 456))
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; {&amp;#34;foo&amp;#34;: {&amp;#34;bar&amp;#34;: 456}}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Mutable mappings, mutable sequences and dataclass instances can be
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; overridden.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;deepcopy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;key_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;key_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;key_path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;__&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# type: ignore[no-redef]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;path_entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;key_path&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MutableMapping&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path_entry&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;KeyError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Key &amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path_entry&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39; not found in target&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;path_entry&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;override_paths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;path_entry&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key_path&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:],&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;path_entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isdigit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MutableSequence&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;override_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path_entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;override_index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;IndexError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;index &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;override_index&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; out of range in target&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;override_index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;override_paths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path_entry&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key_path&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:],&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;has_dataclass_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path_entry&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;setattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;path_entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;override_paths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path_entry&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key_path&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:],&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;KeyError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Key / index / field &amp;#39;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path_entry&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#39; not found in target&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;has_dataclass_field&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;field_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Check if an object is a dataclass that has a dataclass field with the given
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; name.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;is_dataclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nb"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kc"&gt;True&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dataclasses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;field_name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@dataclasses.dataclass&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OverridableMixin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Mixin class which adds a convenience method to override paths on the
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; instance and get back an overridden copy.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; This makes it convenient to override paths inline with a new instance
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; without needing a variable referencing the instance, e.g.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Foobar().overridden(foo__bar__baz=123)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Can be dropped in place to get an instance of Foobar with that path
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; overridden with the new value.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;overridden&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; Get a copy of this instance with the given paths overridden.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;override_paths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Github Action job to post pytest coverage as PR comment</title><link>https://notestoself.dev/posts/github-action-job-pytest-coverage-pr-comment/</link><pubDate>Mon, 23 Sep 2024 18:07:38 +0100</pubDate><guid>https://notestoself.dev/posts/github-action-job-pytest-coverage-pr-comment/</guid><description>&lt;p&gt;Here&amp;rsquo;s a Github Action job that runs pytest coverage and then posts a summary of
the coverage report as a PR comment with Markdown formatting. This is handy as
part of a PR workflow to make the test results and coverage summary visible on
the PR.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;coverage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;coverage&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/checkout@v4&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./.github/actions/setup_python&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./.github/actions/job_url&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;job_url&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;step_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;pytest&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;pytest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; pytest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Test coverage summary report&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;coverage_report&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;always()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo &amp;#34;COVERAGE_REPORT&amp;lt;&amp;lt;EOF&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; coverage report --format=markdown --show-missing --skip-covered
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo &amp;#34;EOF&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; } &amp;gt;&amp;gt; &amp;#34;$GITHUB_OUTPUT&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Remove previous coverage comments&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./.github/actions/remove_comments&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;always()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;coverage&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Test coverage PR comment&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;coverage_comment&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;always()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/github-script@v7&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; github.rest.issues.createComment({
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; owner: context.repo.owner,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; repo: context.repo.repo,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; issue_number: context.issue.number,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; body: `[coverage](${{steps.job_url.outputs.job_url}})
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; \r\n\r\n${{steps.coverage_report.outputs.COVERAGE_REPORT}}`
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; })&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that this uses separate actions to setup the Python env,
&lt;a href="https://notestoself.dev/posts/github-action-job-url/"&gt;get the job URL&lt;/a&gt;
and to
&lt;a href="https://notestoself.dev/posts/github-action-remove-previous-job-pr-comments/"&gt;remove previous PR comments&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Github Action for PR comment with command output on exit status</title><link>https://notestoself.dev/posts/github-action-command-comment-exit-status/</link><pubDate>Mon, 23 Sep 2024 17:50:45 +0100</pubDate><guid>https://notestoself.dev/posts/github-action-command-comment-exit-status/</guid><description>&lt;p&gt;Here&amp;rsquo;s a Github Action that runs a given bash command, captures the stdout and
strderr output, and posts a comment with the output back to the PR if the
command has a non-zero exit status.&lt;/p&gt;
&lt;p&gt;This is useful for running check commands such as linters against a PR, and
making a PR comment with the linter output if there are linting errors.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Command &amp;amp; Comment&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Run a bash command and comment failure output back to PR&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Single word identifier for the check command&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;bash_command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Bash command to run and comment from&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;using&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;composite&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./.github/actions/job_url&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;job_url&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;step_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{inputs.identifier}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;check_command&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{inputs.identifier}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;bash&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; set +e
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo &amp;#34;COMMAND_OUT&amp;lt;&amp;lt;EOF&amp;#34; | tee &amp;#34;$GITHUB_OUTPUT&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; ${{inputs.bash_command}} 2&amp;gt;&amp;amp;1 | sed &amp;#39;s/$/\\n/g&amp;#39; | tr -d &amp;#39;\n&amp;#39; | tee -a &amp;#34;$GITHUB_OUTPUT&amp;#34; ; COMMAND_STATUS=$?
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; ( echo ; echo &amp;#34;EOF&amp;#34; ) | tee -a &amp;#34;$GITHUB_OUTPUT&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; exit $COMMAND_STATUS&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Remove previous command comments&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;./.github/actions/remove_comments&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;always()&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{inputs.identifier}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;PR comment command failure&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/github-script@v7&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;always() &amp;amp;&amp;amp; steps.check_command.outcome == &amp;#39;failure&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; github.rest.issues.createComment({
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; owner: context.repo.owner,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; repo: context.repo.repo,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; issue_number: context.issue.number,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; body: &amp;#39;[${{inputs.identifier}}](${{steps.job_url.outputs.job_url}})\n\n```\n${{steps.check_command.outputs.COMMAND_OUT}}```&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; })&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that this uses separate actions to
&lt;a href="https://notestoself.dev/posts/github-action-job-url/"&gt;get the job URL&lt;/a&gt;
and to
&lt;a href="https://notestoself.dev/posts/github-action-remove-previous-job-pr-comments/"&gt;remove previous PR comments&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Github Action to remove previous job PR comments</title><link>https://notestoself.dev/posts/github-action-remove-previous-job-pr-comments/</link><pubDate>Mon, 23 Sep 2024 14:03:53 +0100</pubDate><guid>https://notestoself.dev/posts/github-action-remove-previous-job-pr-comments/</guid><description>&lt;p&gt;Here&amp;rsquo;s a Github Action that finds and removes PR comments based on a string
identifier. This is useful for Github Action workflows that post comments to
PRs, as it clears up out-of-date comments from earlier workflow runs.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Command &amp;amp; Comment&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Run a bash command and comment failure output back to PR&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Single word identifier for comments to remove&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;using&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;composite&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Remove previous job comments&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/github-script@v7&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;with&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; const pr_comments = await github.rest.issues.listComments({
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; owner: context.repo.owner,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; repo: context.repo.repo,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; issue_number: context.issue.number,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; await Promise.all(
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; pr_comments.data.filter(
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; comment =&amp;gt; comment.body?.startsWith(&amp;#34;[${{inputs.identifier}}]&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; ).map(
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; comment =&amp;gt; github.rest.issues.deleteComment({
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; owner: context.repo.owner,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; repo: context.repo.repo,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; issue_number: context.issue.number,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; comment_id: comment.id,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; })
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; )
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; )&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It removes comments on the PR it is running against if the comment begins with
&lt;code&gt;[{identifier}]&lt;/code&gt;. If other comments happen to begin with that then they will
also be removed. More checks could be added to the filter to avoid this if it&amp;rsquo;s
a problem on a particular project.&lt;/p&gt;</description></item><item><title>Capturing multiline output and exit status in a Github Action with bash</title><link>https://notestoself.dev/posts/github-action-multiline-output-bash/</link><pubDate>Mon, 23 Sep 2024 13:02:14 +0100</pubDate><guid>https://notestoself.dev/posts/github-action-multiline-output-bash/</guid><description>&lt;p&gt;You can capture output from a Github Action by echoing a variable name to the
file specified by the &lt;code&gt;&amp;quot;$GITHUB_OUTPUT&amp;quot;&lt;/code&gt; variable:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;foobar=&amp;#39;here is some data&amp;#39;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; tee -a &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$GITHUB_OUTPUT&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is a bit trickier with multiline output. You can use the bash &lt;code&gt;EOF&lt;/code&gt; syntax
for multiline strings:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;FOOBAR&lt;span class="s"&gt;&amp;lt;&amp;lt;EOF
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;here
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;is
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;some
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;multiline
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;data
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s"&gt;EOF&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The easiest way to use bash multiline strings in a Github Action is to use a
bash command group with curly braces.&lt;/p&gt;</description></item><item><title>Github Action to get the current job URL</title><link>https://notestoself.dev/posts/github-action-job-url/</link><pubDate>Mon, 23 Sep 2024 12:13:40 +0100</pubDate><guid>https://notestoself.dev/posts/github-action-job-url/</guid><description>&lt;p&gt;Here&amp;rsquo;s a Github Action to get the URL to the current job and specific step in
the job to open. This is useful for giving users a direct link to specific jobs.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Get current job URL&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Output the current Github Workflow Job URL&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;step_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Specific job step to include in the URL&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;outputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;job_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;URL to the calling job and specific input step&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{steps.get_job_url.outputs.job_url}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;using&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;composite&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;get_job_url&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;GH_TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{github.token}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;shell&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;bash&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo &amp;#34;job_url=$(gh run --repo ${{github.repository}} view ${{github.run_id}} \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; --json jobs \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; --jq &amp;#39;.jobs[]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; | select(.name == &amp;#34;${{github.job}}&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; | .url,
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; (
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; .steps[]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; | select(.name == &amp;#34;${{inputs.step_name}}&amp;#34;)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; | &amp;#34;#step:\(.number):1&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; )&amp;#39; \
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; | tr -d &amp;#34;\n&amp;#34;)&amp;#34; | tee &amp;#34;$GITHUB_OUTPUT&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The key part is this bash script which uses the &lt;code&gt;gh&lt;/code&gt; CLI tool&amp;rsquo;s &lt;code&gt;--jq&lt;/code&gt; option
to extract the URL:&lt;/p&gt;</description></item><item><title>Ensure Python async generator and context manager closes with contextlib.aclosing</title><link>https://notestoself.dev/posts/python-asyncio-aiofiles-resourcewarning-contextlib-aclosing/</link><pubDate>Wed, 18 Sep 2024 12:42:26 +0100</pubDate><guid>https://notestoself.dev/posts/python-asyncio-aiofiles-resourcewarning-contextlib-aclosing/</guid><description>&lt;p&gt;I noticed a &lt;code&gt;ResourceWarning&lt;/code&gt; like this when using
&lt;a href="https://pypi.org/project/aiofiles/"&gt;aiofiles&lt;/a&gt; to iterate lines of a file in
an async context manager from &lt;code&gt;aiofiles.open&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ResourceWarning: unclosed file
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;lt;_io.TextIOWrapper
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	name=&amp;#39;.../numbers.tmp&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	mode=&amp;#39;r&amp;#39;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	encoding=&amp;#39;UTF-8&amp;#39;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is only a warning, so by default it won&amp;rsquo;t error out of the Python program.
I try to have
&lt;a href="https://docs.pytest.org/en/stable/how-to/capture-warnings.html"&gt;pytest filterwarnings&lt;/a&gt;
set to &lt;code&gt;error&lt;/code&gt; whenever possible, so that any warnings during tests will cause
the test to error out. That&amp;rsquo;s how I spotted this issue.&lt;/p&gt;</description></item><item><title>Python asyncio ensure event loop utility function</title><link>https://notestoself.dev/posts/python-asyncio-ensure-event-loop/</link><pubDate>Sun, 15 Sep 2024 10:51:12 +0100</pubDate><guid>https://notestoself.dev/posts/python-asyncio-ensure-event-loop/</guid><description>&lt;p&gt;Here&amp;rsquo;s a utility function that safely ensures an event loop is running in a
Python asyncio application.&lt;/p&gt;
&lt;p&gt;This is useful because of the discrepancy in expectations between the asyncio
library and how it ends up being used in the real world in a lot of
applications. The asyncio library reasonably expects users to be in control of
the event loop for the whole application lifecycle, but this becomes complicated
in various situations:&lt;/p&gt;</description></item><item><title>Doozy</title><link>https://notestoself.dev/work/doozy/</link><pubDate>Sun, 01 Sep 2024 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/doozy/</guid><description>&lt;p&gt;I&amp;rsquo;ve been helping to build Doozy&amp;rsquo;s product as a freelance Software Engineer,
using GCP, Firebase and TypeScript.&lt;/p&gt;</description></item><item><title>ElseWhen</title><link>https://notestoself.dev/work/elsewhen/</link><pubDate>Mon, 15 Jul 2024 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/elsewhen/</guid><description>&lt;p&gt;I worked as a Software Engineer on a contract basis for ElseWhen, using asyncio,
CloudBuild, CloudRun, GCP, Gemini, Google Cloud Functions, LangChain, LLMs,
OpenAI, PostgreSQL, Python, SQL, Terraform and other technologies.&lt;/p&gt;</description></item><item><title>Omnipresent</title><link>https://notestoself.dev/work/omnipresent/</link><pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/omnipresent/</guid><description>&lt;p&gt;I worked as a Software Engineer on a contract basis for Omnipresent via YLD,
using asyncio, AWS, AWS Lambda, AWS SAM, Docker, ECS, FastAPI, PostgreSQL,
Python, SNS, SQL, SQS, Terraform and other technologies.&lt;/p&gt;</description></item><item><title>AWS CDK to deny release of Elastic IP in Organization SCP</title><link>https://notestoself.dev/posts/aws-org-scp-cdk-deny-release-elastic-ip-eip/</link><pubDate>Sat, 16 Sep 2023 18:23:42 +0000</pubDate><guid>https://notestoself.dev/posts/aws-org-scp-cdk-deny-release-elastic-ip-eip/</guid><description>&lt;p&gt;It&amp;rsquo;s quite likely that you&amp;rsquo;ll let other organisations know about the
&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html"&gt;Elastic IPs&lt;/a&gt;
that you have configured in your AWS VPC. The other organisations could be
service providers or your customers, and they might not have automated processes
for configuring these static IP addresses on their side (sending configurations by
email is surprisingly common).&lt;/p&gt;
&lt;p&gt;This creates a risk around the accidental release of Elastic IPs, especially if
you manage your Elastic IPs with CloudFormation or CDK. Updating a
CloudFormation stack that manages an Elastic IP could accidentally release that
Elastic IP and recreate the resource with a different IP address. This would
cause an immediate outage or disruption in any service that was configured with
the released Elastic IP.&lt;/p&gt;</description></item><item><title>AWS CDK for EC2 bastion host sharing Elastic IP with rest of VPC</title><link>https://notestoself.dev/posts/aws-cdk-ec2-bastion-host-vpc-port-forward/</link><pubDate>Wed, 16 Aug 2023 18:23:42 +0000</pubDate><guid>https://notestoself.dev/posts/aws-cdk-ec2-bastion-host-vpc-port-forward/</guid><description>&lt;p&gt;Here&amp;rsquo;s a small CDK stack to set up an EC2 instance as a bastion host inside a
VPC, sharing a fixed Elastic IP with other services in the VPC via the NAT
Gateway.&lt;/p&gt;
&lt;p&gt;A bastion host is useful for connecting to your own services inside the VPC.
With this particular setup, you can also use the same bastion host to connect to
external services that require a fixed IP address on your side.&lt;/p&gt;</description></item><item><title>Minify JSON clipboard contents back to clipboard with Bash and Python</title><link>https://notestoself.dev/posts/ubuntu-bash-python-minify-json-clipboard-cli/</link><pubDate>Tue, 18 Jul 2023 13:18:51 +0100</pubDate><guid>https://notestoself.dev/posts/ubuntu-bash-python-minify-json-clipboard-cli/</guid><description>&lt;p&gt;Here&amp;rsquo;s a Bash one-liner to minify JSON in the current contents of the clipboard and
replace the clipboard contents with the minified JSON:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;xclip -selection clipboard -o &lt;span class="p"&gt;|&lt;/span&gt; python3 -c &lt;span class="s2"&gt;&amp;#34;import sys;import json;print(json.dumps(json.loads(sys.stdin.read()),separators=(\&amp;#34;,\&amp;#34;,\&amp;#34;:\&amp;#34;)))&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; tee &amp;gt;&lt;span class="o"&gt;(&lt;/span&gt;xclip -selection clipboard&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The equivalent on Mac is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pbpaste &lt;span class="p"&gt;|&lt;/span&gt; python3 -c &lt;span class="s2"&gt;&amp;#34;import sys;import json;print(json.dumps(json.loads(sys.stdin.read()),separators=(\&amp;#34;,\&amp;#34;,\&amp;#34;:\&amp;#34;)))&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; pbcopy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To set it as an alias in Linux (&lt;code&gt;mcj&lt;/code&gt; for &amp;ldquo;minify clipboard json&amp;rdquo;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;mcj&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;xclip -selection clipboard -o | python3 -c &amp;#34;import sys;import json;print(json.dumps(json.loads(sys.stdin.read()),separators=(\&amp;#34;,\&amp;#34;,\&amp;#34;:\&amp;#34;)))&amp;#34; | tee &amp;gt;(xclip -selection clipboard)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then you can copy some JSON, run &lt;code&gt;mcj&lt;/code&gt;, and then paste the minified JSON back
from the clipboard, e.g.&lt;/p&gt;</description></item><item><title>JSON encode clipboard contents back to clipboard with Bash and Python</title><link>https://notestoself.dev/posts/ubuntu-bash-python-json-encode-clipboard-cli/</link><pubDate>Tue, 18 Jul 2023 12:38:51 +0100</pubDate><guid>https://notestoself.dev/posts/ubuntu-bash-python-json-encode-clipboard-cli/</guid><description>&lt;p&gt;Here&amp;rsquo;s a Bash one-liner to JSON-encode the current contents of the clipboard and
replace the clipboard contents with the JSON-encoded output:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;xclip -o &lt;span class="p"&gt;|&lt;/span&gt; python -c &lt;span class="s1"&gt;&amp;#39;import sys;import json;print(json.dumps(sys.stdin.read().strip()))&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; tee &amp;gt;&lt;span class="o"&gt;(&lt;/span&gt;xclip -selection clipboard&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The equivalent on Mac is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pbpaste &lt;span class="p"&gt;|&lt;/span&gt; python -c &lt;span class="s1"&gt;&amp;#39;import sys;import json;print(json.dumps(sys.stdin.read().strip()))&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; pbcopy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To set it as an alias in Linux (&lt;code&gt;jec&lt;/code&gt; for &amp;ldquo;json encode clipboard&amp;rdquo;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;jec&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;xclip -selection clipboard -o | python -c &amp;#34;import sys;import json;print(json.dumps(sys.stdin.read().strip()))&amp;#34; | tee &amp;gt;(xclip -selection clipboard)&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then you can copy some string text, run &lt;code&gt;jec&lt;/code&gt;, and then paste the JSON-encoded
string back from the clipboard.&lt;/p&gt;</description></item><item><title>AWS EC2 bastion host SSM multi port forwarding bash script</title><link>https://notestoself.dev/posts/aws-ec2-bastion-host-ssm-multi-port-forward-bash/</link><pubDate>Fri, 16 Jun 2023 18:23:42 +0000</pubDate><guid>https://notestoself.dev/posts/aws-ec2-bastion-host-ssm-multi-port-forward-bash/</guid><description>&lt;p&gt;Here&amp;rsquo;s a small bash script to identify an EC2 instance as a
&lt;a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/patterns/access-a-bastion-host-by-using-session-manager-and-amazon-ec2-instance-connect.html"&gt;bastion host&lt;/a&gt;,
and then use SSM Session Manager to forward multiple ports through the bastion
on to other hosts.&lt;/p&gt;
&lt;p&gt;This is useful when you want to access hosts inside your VPC (such as RDS /
Aurora), external services that enforce an IP access list (such as MongoDB
Atlas), or a mixture of those.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve used scripts like this to run integration tests that talk to combinations
of services like the above.&lt;/p&gt;</description></item><item><title>KrakenFlex</title><link>https://notestoself.dev/work/krakenflex/</link><pubDate>Thu, 01 Jun 2023 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/krakenflex/</guid><description>&lt;p&gt;I worked as a Software Engineer on a contract basis for KrakenFlex, using
asyncio, AWS, AWS Lambda, AWS SAM, CloudFormation, DynamoDB, Python, SNS, SQS
and other technologies.&lt;/p&gt;</description></item><item><title>AWS SAM CloudFormation SSM Parameter secure string not supported workaround</title><link>https://notestoself.dev/posts/aws-sam-cloudformation-ssm-parameter-secure-string-not-supported-workaround/</link><pubDate>Tue, 16 May 2023 18:23:42 +0000</pubDate><guid>https://notestoself.dev/posts/aws-sam-cloudformation-ssm-parameter-secure-string-not-supported-workaround/</guid><description>&lt;p&gt;It&amp;rsquo;s surprisingly difficult to use AWS SSM Parameter secure strings in
CloudFormation templates.&lt;/p&gt;
&lt;p&gt;If you try and have CloudFormation fetch the value of the SSM Parameter as a
&lt;code&gt;AWS::SSM::Parameter::Value&amp;lt;String&amp;gt;&lt;/code&gt; type, you&amp;rsquo;ll get this error:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;An error occurred (ValidationError) when calling the CreateStack operation:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Parameters [/foobar/foo_param] referenced by template have types not supported
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;by CloudFormation.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you try and have a CloudFormation parameter of type
&lt;code&gt;AWS::SSM::Parameter::Value&amp;lt;SecureString&amp;gt;&lt;/code&gt;, you&amp;rsquo;ll get this error:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;An error occurred (ValidationError) when calling the CreateStack operation:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Template format error: Unrecognized parameter type: SecureString
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you try and use the special &lt;code&gt;resolve&lt;/code&gt; function in the CloudFormation template,
like this:&lt;/p&gt;</description></item><item><title>AWS Lambda Python SAM build with container and CodeArtifact poetry auth</title><link>https://notestoself.dev/posts/aws-lambda-python-poetry-codeartifact-auth-sam-container-build/</link><pubDate>Sun, 16 Apr 2023 18:23:42 +0000</pubDate><guid>https://notestoself.dev/posts/aws-lambda-python-poetry-codeartifact-auth-sam-container-build/</guid><description>&lt;p&gt;It&amp;rsquo;s often beneficial to use Docker to build AWS SAM Lambda Function images.
Projects using AWS SAM often also use AWS CodeArtifact to manage private
libraries, and poetry to manage Python dependencies.&lt;/p&gt;
&lt;p&gt;This combination can make it a little bit tricky to get the index auth working
during the Docker build.&lt;/p&gt;
&lt;p&gt;Your Dockerfile for building the AWS SAM Lambda Function might look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-docker" data-lang="docker"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;public.ecr.aws/lambda/python:3.12&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ENV&lt;/span&gt; &lt;span class="nv"&gt;PIP_DISABLE_PIP_VERSION_CHECK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;on &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;PYTHONDONTWRITEBYTECODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;PYTHONUNBUFFERED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;COPY&lt;/span&gt; requirements.txt .&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;RUN&lt;/span&gt; python3.12 -m pip install -r requirements.txt&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;RUN&lt;/span&gt; rm requirements.txt&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;RUN&lt;/span&gt; mkdir -p &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LAMBDA_TASK_ROOT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/foobar_lambda&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;COPY&lt;/span&gt; foobar_lambda &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LAMBDA_TASK_ROOT&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/foobar_lambda&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CMD&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;foobar_lambda.aws.lambda_handler&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can build the AWS SAM Lambda Function image from that, using an
authenticated private repository in AWS CodeArtifact, with a shell script like
this:&lt;/p&gt;</description></item><item><title>Converting PDF slides to PNG then CSV to bulk import into Anki</title><link>https://notestoself.dev/posts/pdf-png-csv-anki/</link><pubDate>Fri, 17 Mar 2023 10:40:47 +0000</pubDate><guid>https://notestoself.dev/posts/pdf-png-csv-anki/</guid><description>&lt;p&gt;I had a large slide deck in PDF format and wanted to add the individual slides
to Anki to revise from. There is some benefit to the learning process in doing
this manually, but it was more practical to study the slides first and then
automate the import into Anki.&lt;/p&gt;
&lt;p&gt;The final bash command to produce an importable CSV file for Anki looks like
this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pdftoppm my_slides.pdf my_slide &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; -progress -png -f &lt;span class="m"&gt;20&lt;/span&gt; -l &lt;span class="m"&gt;923&lt;/span&gt; -rx &lt;span class="m"&gt;60&lt;/span&gt; -ry &lt;span class="m"&gt;60&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cp ./*.png ~/.local/share/Anki2/my_username/collection.media/ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &amp;gt;&lt;span class="p"&gt;|&lt;/span&gt; my_list.csv &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ls *.png &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{printf(&amp;#34;&amp;lt;img src=&amp;#39;&lt;/span&gt;%s&lt;span class="s1"&gt;&amp;#39;/&amp;gt;\t\tmy_tag_1 my_tag_2\n&amp;#34;,$1)}&amp;#39;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;gt;&amp;gt; my_list.csv
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;First it uses the &lt;code&gt;pdftoppm&lt;/code&gt; tool to create a PNG image file for each slide:&lt;/p&gt;</description></item><item><title>Unit testing a Python AWS Lambda handler function that uses asyncio with its own event loop</title><link>https://notestoself.dev/posts/python-pytest-asyncio-aws-lambda-handler-event-loop/</link><pubDate>Fri, 17 Feb 2023 10:40:47 +0000</pubDate><guid>https://notestoself.dev/posts/python-pytest-asyncio-aws-lambda-handler-event-loop/</guid><description>&lt;p&gt;If you&amp;rsquo;re using asyncio in an AWS Lambda, the Lambda handler function has to
manage the asyncio event loop inside the Lambda, as the handler function itself
cannot be an async function.&lt;/p&gt;
&lt;p&gt;This is fairly easy to do by having the top-level Lambda handler function use
&lt;code&gt;asyncio.run&lt;/code&gt; to call an asynchronous function that does the actual work:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;asyncio&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foobar_handler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;foobar&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Or you might be using AWS Lambda Powertools to do async batch processing:&lt;/p&gt;</description></item><item><title>Python AWS boto moto SNS Invalid Parameter Exception: 'not enough values to unpack (expected 6, got 1)'</title><link>https://notestoself.dev/posts/python-aws-boto-moto-sns-invalid-parameter-unpack-6-arn/</link><pubDate>Tue, 17 Jan 2023 10:40:47 +0000</pubDate><guid>https://notestoself.dev/posts/python-aws-boto-moto-sns-invalid-parameter-unpack-6-arn/</guid><description>&lt;p&gt;I just spent a long time debugging this error I was getting in a unit test using
moto to mock out the boto3 SNS client:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;botocore.errorfactory.InvalidParameterException: An error occurred
(InvalidParameter) when calling the Publish operation: not enough values to
unpack (expected 6, got 1)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This error comes from the innards of moto, where it splits an ARN on &lt;code&gt;:&lt;/code&gt; and
assumes there will be six sections to unpack into Python variables there. I was
using &lt;code&gt;test_sns_topic_arn&lt;/code&gt; as this is just a unit test.&lt;/p&gt;</description></item><item><title>Hyperobjekt</title><link>https://notestoself.dev/work/hyperobjekt/</link><pubDate>Thu, 01 Sep 2022 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/hyperobjekt/</guid><description/></item><item><title>Pytest fixture with optional parameter change inside test case</title><link>https://notestoself.dev/posts/pytest-fixture-optional-change-parameter-inside-test-case/</link><pubDate>Mon, 18 Apr 2022 12:34:32 +0000</pubDate><guid>https://notestoself.dev/posts/pytest-fixture-optional-change-parameter-inside-test-case/</guid><description>&lt;p&gt;Here&amp;rsquo;s a way of setting up a pytest fixture that sets up a default value
automatically for any test case that uses the fixture, but allows calling the
fixture from inside the test case to update that value where necessary.&lt;/p&gt;
&lt;p&gt;The example is for mocking out API responses with &lt;code&gt;httpx_mock&lt;/code&gt;, but this fixture
arrangement can be used for any situation where you want a default fixture value
that can optionally be modified from within a single test case.&lt;/p&gt;</description></item><item><title>Python assert_dict_matches recursive test helper function</title><link>https://notestoself.dev/posts/python-assert_dict_matches-recursive-test-helper-function/</link><pubDate>Fri, 18 Mar 2022 12:34:32 +0000</pubDate><guid>https://notestoself.dev/posts/python-assert_dict_matches-recursive-test-helper-function/</guid><description>&lt;p&gt;The Jest library for Node.js has a helpful
&lt;a href="https://jestjs.io/docs/expect#expectobjectcontainingobject"&gt;expect.objectContaining&lt;/a&gt;
function that makes it easy to assert only on the keys of interest in a
particular test.&lt;/p&gt;
&lt;p&gt;Python&amp;rsquo;s &lt;code&gt;unittest&lt;/code&gt; library does have a similar
&lt;a href="https://docs.python.org/3/library/unittest.html#unittest.TestCase.assertDictEqual"&gt;assertDictEqual&lt;/a&gt;
method, but it asserts on all keys.&lt;/p&gt;
&lt;p&gt;I wanted to do be able to easily assert on only the keys of interest in Python
tests, so I put together this quick test helper function to do so. It makes use
of &lt;code&gt;assertDictEqual&lt;/code&gt;, but only after extracting the keys of interest in a
recursive way.&lt;/p&gt;</description></item><item><title>Hugo asset pipeline security.exec.allow error</title><link>https://notestoself.dev/posts/hugo-asset-pipeline-security.exec.allow-error/</link><pubDate>Tue, 15 Mar 2022 16:19:31 +0000</pubDate><guid>https://notestoself.dev/posts/hugo-asset-pipeline-security.exec.allow-error/</guid><description>&lt;p&gt;If you hit this error with recent versions of Hugo:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Error: Error building site: BABEL: failed to transform &amp;#34;js/main.js&amp;#34; (application/javascript): access denied: &amp;#34;babel&amp;#34; is not whitelisted in policy &amp;#34;security.exec.allow&amp;#34;; the current security configuration is:
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You need to allow &lt;code&gt;babel&lt;/code&gt; in Hugo&amp;rsquo;s security rules in the config file, like
this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;security&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;security&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="nx"&gt;allow&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;^go$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;^npx$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;^postcss$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;^babel$&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Export, clipboard copy and echo a token Bash one-liner</title><link>https://notestoself.dev/posts/export-copy-clipboard-echo-token-bash/</link><pubDate>Tue, 15 Feb 2022 16:19:31 +0000</pubDate><guid>https://notestoself.dev/posts/export-copy-clipboard-echo-token-bash/</guid><description>&lt;p&gt;Here&amp;rsquo;s a handy Bash one-liner to generate a token or some other kind of command
output, and do three things at once with it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Export it as an environment variable in the shell.&lt;/li&gt;
&lt;li&gt;Copy it to the clipboard.&lt;/li&gt;
&lt;li&gt;Echo it to the terminal.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;FOOBAR_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;foobar-token-command --foobar &lt;span class="s2"&gt;&amp;#34;foobar&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; -n &lt;span class="nv"&gt;$FOOBAR_TOKEN&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; tee &amp;gt;&lt;span class="o"&gt;(&lt;/span&gt;xclip -selection clipboard&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I find this is handy for things like security tokens which I often want as an
environment variable in the shell as well as being able to paste them into other
tools such as HTTP testing apps.&lt;/p&gt;</description></item><item><title>Hugo new post Bash function</title><link>https://notestoself.dev/posts/hugo-new-post-bash-function/</link><pubDate>Sat, 15 Jan 2022 12:52:06 +0000</pubDate><guid>https://notestoself.dev/posts/hugo-new-post-bash-function/</guid><description>&lt;p&gt;Hugo has a &lt;a href="https://gohugo.io/commands/hugo_new/"&gt;hugo new&lt;/a&gt; helper command that
can generate new content files for you. It doesn&amp;rsquo;t quite do what I want, though,
so I&amp;rsquo;ve added this quick Bash function to my &lt;code&gt;.profile&lt;/code&gt; to make it easier to
use:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;hnp&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; hugo new &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;posts&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;date +%Y&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;date +%m&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/index.md&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;All it does is create a new post file in its own content directory at a
date-based path, which is the structure I use for my Hugo sites.&lt;/p&gt;</description></item><item><title>AWS DocumentDB data source in JetBrains DataGrip</title><link>https://notestoself.dev/posts/jetbrains-datagrip-aws-documentdb-connection/</link><pubDate>Thu, 06 Jan 2022 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/jetbrains-datagrip-aws-documentdb-connection/</guid><description>&lt;p&gt;I&amp;rsquo;m currently working on a project for Kensio Software using
&lt;a href="https://aws.amazon.com/documentdb/"&gt;AWS DocumentDB&lt;/a&gt;, and wanted to access the
database for development using DataGrip. Here are some quick notes on how to set
up a DocumentDB database as a data source in DataGrip.&lt;/p&gt;
&lt;h2 id="getting-documentdb-connection-details-from-aws-console"&gt;Getting DocumentDB connection details from AWS console&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s probably easiest to get the connection details for the DocumentDB cluster
from the AWS Console.&lt;/p&gt;
&lt;p&gt;At the time of writing, the URL for the DocumentDB cluster looks like this:&lt;/p&gt;</description></item><item><title>X2X Media</title><link>https://notestoself.dev/work/x2x-media/</link><pubDate>Sat, 01 Jan 2022 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/x2x-media/</guid><description/></item><item><title>Python iterator chunk generator function</title><link>https://notestoself.dev/posts/python-iterator-chunk-generator-function/</link><pubDate>Sat, 23 Oct 2021 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/python-iterator-chunk-generator-function/</guid><description>&lt;p&gt;Here&amp;rsquo;s a versatile Python function that splits any iterator into chunks of
size &lt;em&gt;n&lt;/em&gt; as a generator. It can take simple iterators such as lists, but also
take a generator and chunk it without unwinding the whole thing up front.&lt;/p&gt;
&lt;p&gt;This is quite useful when dealing with a database cursor, for example. You can
iterate through the cursor in chunks of size &lt;em&gt;n&lt;/em&gt; without having to load all of
the objects into memory at once.&lt;/p&gt;</description></item><item><title>Simple example of a floating point precision error for monetary amounts</title><link>https://notestoself.dev/posts/simple-example-floating-point-precision-error-monetary-amount/</link><pubDate>Sat, 23 Oct 2021 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/simple-example-floating-point-precision-error-monetary-amount/</guid><description>&lt;p&gt;Most software engineers are aware that floating point numbers are
&lt;a href="https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems"&gt;not suitable&lt;/a&gt;
for monetary amounts, or any amount requiring reliable decimal precision.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s nice to have a quick and simple example of this, so here&amp;rsquo;s an operation
that will break with floating point numbers in many languages including
JavaScript:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="mf"&gt;1.01&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.42&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// → 0.5900000000000001
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In JavaScript and TypeScript, the &lt;a href="https://github.com/MikeMcl/big.js/"&gt;big.js&lt;/a&gt;
library is good for decimal operations.&lt;/p&gt;
&lt;p&gt;In SQL, it&amp;rsquo;s best to store monetary and other decimal amounts as &lt;code&gt;DECIMAL&lt;/code&gt;, e.g.
&lt;code&gt;DECIMAL(10,2)&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Explicit types are a better habit than type inference</title><link>https://notestoself.dev/posts/explicit-types-better-habit-type-inference-typescript/</link><pubDate>Thu, 02 Sep 2021 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/explicit-types-better-habit-type-inference-typescript/</guid><description>&lt;p&gt;TypeScript has a handy
&lt;a href="https://www.typescriptlang.org/docs/handbook/type-inference.html"&gt;type inference&lt;/a&gt;
feature where the compiler will automatically determine types in most situations.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;fooBar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fooBar&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// x&amp;#39;s type is infered as number
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is quite useful and potentially allows for tidier, more readable code.
However, I would actually suggest that avoiding type inference and preferring
explicit types is a good habit.&lt;/p&gt;
&lt;p&gt;For trivial examples like the above, it probably is overkill to declare &lt;code&gt;x&lt;/code&gt; as
&lt;code&gt;const x: number = 42&lt;/code&gt;. Preferring explicit typing is a valuable habit in
larger projects with more complexity. While human readers will lose track of
long type chains, the compiler will happily keep infering them to the point
where it&amp;rsquo;s difficult to debug and maintain.&lt;/p&gt;</description></item><item><title>Google Cloud Platform Logging Query Syntax Exploration Fu</title><link>https://notestoself.dev/posts/gcp-google-cloud-platform-logging-query-syntax-exploration-fu/</link><pubDate>Wed, 04 Aug 2021 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/gcp-google-cloud-platform-logging-query-syntax-exploration-fu/</guid><description>&lt;p&gt;Recently I&amp;rsquo;ve been working in Google Cloud Platform (GCP) in a highly
distributed system that is partitioned into many different services with
various deployment strategies. A lot of it runs in Google Kubernetes Engine
(GKE), a lot of it runs in Google Cloud Functions (GCF), and a lot of it runs
in other bits and pieces of GCP infrastructure. Even the areas that are managed
via GKE and Cloud Functions are heterogenous, with many idiosyncratic
infrastructure configurations.&lt;/p&gt;</description></item><item><title>My Jest mock cheatsheet for TypeScript</title><link>https://notestoself.dev/posts/jest-mock-cheatsheet-typescript/</link><pubDate>Wed, 23 Jun 2021 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/jest-mock-cheatsheet-typescript/</guid><description>&lt;p&gt;I find myself looking up past projects to remember how to mock various things
with Jest in TypeScript, so I&amp;rsquo;ve started writing up notes on this page to gather
them in one place.&lt;/p&gt;
&lt;h2 id="mock-a-function-without-early-referencing"&gt;Mock a function without early referencing&lt;/h2&gt;
&lt;p&gt;Because of the way
&lt;a href="https://github.com/kentcdodds/how-jest-mocking-works"&gt;Jest hoists mocks&lt;/a&gt;, it&amp;rsquo;s
easy to trigger a &lt;a href="https://en.wiktionary.org/wiki/footgun"&gt;footgun&lt;/a&gt; where Jest
complains about referencing something before initialisation.&lt;/p&gt;
&lt;p&gt;You can usually avoid that by wrapping the reference in an outer function call,
so that it&amp;rsquo;s not immediately referenced:&lt;/p&gt;</description></item><item><title>Udemy Course Progress JS Bookmarklet</title><link>https://notestoself.dev/posts/udemy-course-progress-js-bookmarklet/</link><pubDate>Sat, 05 Jun 2021 12:16:20 +0100</pubDate><guid>https://notestoself.dev/posts/udemy-course-progress-js-bookmarklet/</guid><description>&lt;p&gt;The Udemy video course UI doesn&amp;rsquo;t give you a lot of information about your
progress through a course. I find it helpful to be able to see progress and the
amount of time remaining in a course, so I made this JavaScript bookmarklet to
display some figures about progress through a Udemy course.&lt;/p&gt;
&lt;p&gt;You need to be on the Udemy video player page when you trigger the bookmarklet.
It will display progress stats in a browser alert.&lt;/p&gt;</description></item><item><title>Bulb</title><link>https://notestoself.dev/work/bulb/</link><pubDate>Tue, 01 Jun 2021 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/bulb/</guid><description>&lt;p&gt;I worked as a Software Engineer on a contract basis for Bulb via YLD, using
Kubernetes, Terraform, GCP, TypeScript, Node.js, Python, MySQL, PostgreSQL and
other technologies.&lt;/p&gt;</description></item><item><title>Sweeping complexity under the infrastructure carpet</title><link>https://notestoself.dev/posts/sweep-complexity-under-infrastructure-carpet/</link><pubDate>Sun, 23 May 2021 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/sweep-complexity-under-infrastructure-carpet/</guid><description>&lt;p&gt;Some projects seem to get into the habit of keeping the application code
relatively clean and simple by hiding complexity in the infrastructure layer.
My made up term for this is &lt;strong&gt;sweeping complexity under the infrastructure
carpet&lt;/strong&gt;. It feels appropriate that this is a bit of a mouthful to say.&lt;/p&gt;
&lt;p&gt;This anti-pattern seems to creep in via an attitude that complexity in
infrastructure &amp;ldquo;doesn&amp;rsquo;t count&amp;rdquo;, so it&amp;rsquo;s OK to let it grow and fester there
while the application code appears to be more healthy. Maybe complex
infrastructure is even a goal to aspire to, as working with high-powered cloud
architecture components seems more worthy than tinkering with classes and
functions in application land.&lt;/p&gt;</description></item><item><title>MySQL timestamp columns default to ON UPDATE CURRENT TIMESTAMP</title><link>https://notestoself.dev/posts/mysql-timestamp-default-update-current-timestamp/</link><pubDate>Tue, 18 May 2021 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/mysql-timestamp-default-update-current-timestamp/</guid><description>&lt;p&gt;A quick note about a &lt;a href="https://en.wiktionary.org/wiki/footgun"&gt;footgun&lt;/a&gt; in MySQL
with &lt;code&gt;timestamp&lt;/code&gt; colummns, which I happened to notice when using migrations in
the &lt;a href="https://knexjs.org/"&gt;knex.js&lt;/a&gt; library.&lt;/p&gt;
&lt;p&gt;TLDR: older versions of MySQL (&amp;lt;= 5.7) default to adding a
&lt;code&gt;DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP&lt;/code&gt; clause to &lt;code&gt;timestamp&lt;/code&gt;
columns unless otherwise specified.&lt;/p&gt;
&lt;p&gt;Documentation here: &lt;a href="https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_explicit_defaults_for_timestamp"&gt;https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_explicit_defaults_for_timestamp&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For example, if you create a table like this in older versions of MySQL:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;foobar_table&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="n"&gt;foobar_column&lt;/span&gt;&lt;span class="o"&gt;`&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;timestamp&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You&amp;rsquo;ll actually get a column like this:&lt;/p&gt;</description></item><item><title>TypeScript branded string type utility for type safety</title><link>https://notestoself.dev/posts/typescript-branded-string-type-utility-safety/</link><pubDate>Wed, 28 Apr 2021 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/typescript-branded-string-type-utility-safety/</guid><description>&lt;p&gt;Here&amp;rsquo;s an easy way to get branded string types in TypeScript. This allows the
compiler to enforce minimal type safety on strings with an identity such as a
User ID or Post ID.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Brand&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;K&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;T&lt;/span&gt; &lt;span class="na"&gt;extends&lt;/span&gt; &lt;span class="na"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;K&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;P&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;never&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This can be helpful when you have multiple kinds of IDs that are all strings at
runtime, and you want to avoid accidentally passing them in the wrong place.&lt;/p&gt;
&lt;p&gt;It also lets types in consuming code offer some documentation of interfaces, for
example:&lt;/p&gt;</description></item><item><title>Static site full text search with Hugo, S3, Python and JS</title><link>https://notestoself.dev/posts/static-site-full-text-search-hugo-s3-python-js/</link><pubDate>Mon, 26 Apr 2021 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/static-site-full-text-search-hugo-s3-python-js/</guid><description>&lt;p&gt;Static sites are great for their speed and ease of deployment at low cost. One
thing they might seem to miss out on is full-text search, but you can actually
achieve reasonable full-text search functionality on a static site with a fairly
simple solution.&lt;/p&gt;
&lt;p&gt;This approach can be expanded in various ways to offer more functionality, but
the basic core of it is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Use Python to iterate all of the searchable items, and create an &lt;code&gt;index.json&lt;/code&gt;
file for each term found.&lt;/li&gt;
&lt;li&gt;Deploy those &lt;code&gt;index.json&lt;/code&gt; files with the rest of the static site to S3.&lt;/li&gt;
&lt;li&gt;Create a static file at &lt;code&gt;/search/index.html&lt;/code&gt; with a JS script to handle the
search behaviour.&lt;/li&gt;
&lt;li&gt;That JS script tries to fetch the &lt;code&gt;index.json&lt;/code&gt; file for each search term,
combines all the items found, scores them by how many index files they appear
in and sorts them by that score.&lt;/li&gt;
&lt;li&gt;Finally the JS script inserts a DOM element for each item using the data it
got from the index.json files.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You can see a demo of this static site search here: &lt;a href="https://momentwallart.co.uk/search/?q=japanese+mountains"&gt;https://momentwallart.co.uk/search/?q=japanese+mountains&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Selecting the best available social meta data image in Hugo</title><link>https://notestoself.dev/posts/hugo-default-page-resource-social-image/</link><pubDate>Fri, 23 Apr 2021 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/hugo-default-page-resource-social-image/</guid><description>&lt;p&gt;Here&amp;rsquo;s a snippet of Hugo template language that I use to try and select the best
social meta data image for each page on a static site:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ if isset .Params &amp;#34;meta_image&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;og:image&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .Params.meta_image | absURL }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;twitter:image&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .Params.meta_image | absURL }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;image_src&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .Params.meta_image | absURL }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;itemprop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;image&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .Params.meta_image | absURL }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ else if (.Resources.ByType &amp;#34;image&amp;#34;) | len }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ $firstImage := index (.Resources.ByType &amp;#34;image&amp;#34;) 0 }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;og:image&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ $firstImage.Permalink }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;twitter:image&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ $firstImage.Permalink }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;image_src&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ $firstImage.Permalink }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;itemprop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;image&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ $firstImage.Permalink }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ else }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;property&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;og:image&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;img&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;default-image&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;png&lt;/span&gt;&lt;span class="err"&gt;&amp;#34;&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="na"&gt;absURL&lt;/span&gt; &lt;span class="err"&gt;}}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;twitter:image&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;img&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;default-image&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;png&lt;/span&gt;&lt;span class="err"&gt;&amp;#34;&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="na"&gt;absURL&lt;/span&gt; &lt;span class="err"&gt;}}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="na"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;image_src&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;img&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;default-image&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;png&lt;/span&gt;&lt;span class="err"&gt;&amp;#34;&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="na"&gt;absURL&lt;/span&gt; &lt;span class="err"&gt;}}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;itemprop&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;image&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;img&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;default-image&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="na"&gt;png&lt;/span&gt;&lt;span class="err"&gt;&amp;#34;&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="na"&gt;absURL&lt;/span&gt; &lt;span class="err"&gt;}}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;First it tries using a &lt;code&gt;meta_image&lt;/code&gt; key in the page&amp;rsquo;s frontmatter, then the
first image resource if one is available, and finally falls back to a default
global meta image if neither of those are present.&lt;/p&gt;</description></item><item><title>Connecting to an old modp1024 L2TP Ipsec VPN on Ubuntu 21.04</title><link>https://notestoself.dev/posts/ubuntu-21.04-connect-old-modp1024-l2tp-ipsec-vpn/</link><pubDate>Thu, 01 Apr 2021 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/ubuntu-21.04-connect-old-modp1024-l2tp-ipsec-vpn/</guid><description>&lt;p&gt;A client of &lt;a href="https://kensiosoftware.co.uk"&gt;Kensio Software&lt;/a&gt; is using an L2TP VPN
with Ipsec that&amp;rsquo;s using an obsolete DH2 (modp1024) algorithm. This algorithm
&lt;a href="https://github.com/nm-l2tp/NetworkManager-l2tp/wiki/Known-Issues#libreswan-algorithm-modp1024-is-not-supported-error"&gt;was considered insecure&lt;/a&gt;
as far back as 2001, and has now been deprecated in encryption libraries such as
&lt;a href="https://github.com/libreswan/libreswan"&gt;libreswan&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Most of the permanent engineers at the client are on MacOS, which still supports
DH2 / modp1024 algorithm out of the box. I was able to get the VPN connected on
a Macbook, but still wanted to try and get it working on Ubuntu as that&amp;rsquo;s my
main working OS.&lt;/p&gt;</description></item><item><title>Beware of SQL averages over dates with empty days</title><link>https://notestoself.dev/posts/beware-sql-average-empty-days/</link><pubDate>Tue, 23 Feb 2021 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/beware-sql-average-empty-days/</guid><description>&lt;p&gt;It&amp;rsquo;s pretty common to use SQL to extract sales summaries from tables that
represent sales orders as rows. These kinds of queries often use the
&lt;a href="https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_avg"&gt;AVG()&lt;/a&gt;
aggregate function to summarise the sales volume. There is a major pitfall when
doing this, though!&lt;/p&gt;
&lt;p&gt;We might run a sub-query like this to get daily revenue totals from the orders
table:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order_total&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;total_revenue&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;GROUP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Those aggregated revenue query results might look something like this:&lt;/p&gt;</description></item><item><title>Generating a range of dates in MySQL</title><link>https://notestoself.dev/posts/mysql-generate-date-range/</link><pubDate>Sat, 23 Jan 2021 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/mysql-generate-date-range/</guid><description>&lt;p&gt;It&amp;rsquo;s often useful to have a range of contiguous dates when building SQL queries,
for example by using a left join to ensure that data from other tables that
might have missing dates will get a null value in those rows instead of
skipping the rows entirely.&lt;/p&gt;
&lt;p&gt;Postgres has its wonderful
&lt;a href="https://www.postgresql.org/docs/9.1/functions-srf.html"&gt;generate_series&lt;/a&gt;
function which can solve a lot of
these problems. Unfortunately MySQL doesn&amp;rsquo;t have the same function.&lt;/p&gt;
&lt;p&gt;You can generate a range of contiguous dates in MySQL with a query like this:&lt;/p&gt;</description></item><item><title>Simple Boto3 mock in Python with Pytest monkeypatch</title><link>https://notestoself.dev/posts/simple-boto3-mock-pytest-monkeypatch/</link><pubDate>Wed, 20 Jan 2021 17:13:05 +0000</pubDate><guid>https://notestoself.dev/posts/simple-boto3-mock-pytest-monkeypatch/</guid><description>&lt;p&gt;If you&amp;rsquo;re anything like me, then you might find mocking dependencies in Python
tests quite fiddly and unintuitive (it&amp;rsquo;s certainly less straightforward than
Jest in TypeScript).&lt;/p&gt;
&lt;p&gt;I needed to mock one part of the Python boto3 library for AWS, and I got it
working using Pytest&amp;rsquo;s monkeypatch.&lt;/p&gt;
&lt;p&gt;This shows a test for fetching a value from a JSON-encoded secret string in AWS
Secrets Manager, but you can extrapolate from this roughly how to mock out other
boto3 APIs in a similar way.&lt;/p&gt;</description></item><item><title>Big Sister, Little Sister, Red Sister</title><link>https://notestoself.dev/reading/big-sister-little-sister-red-sister/</link><pubDate>Fri, 01 Jan 2021 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/big-sister-little-sister-red-sister/</guid><description/></item><item><title>Stubborn Attachments</title><link>https://notestoself.dev/reading/stubborn-attachments/</link><pubDate>Fri, 01 Jan 2021 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/stubborn-attachments/</guid><description/></item><item><title>ESLint custom rules are useful</title><link>https://notestoself.dev/posts/eslint-custom-rules/</link><pubDate>Wed, 23 Dec 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/eslint-custom-rules/</guid><description>&lt;p&gt;Here&amp;rsquo;s an example of a custom eslint rule that detects usage of the &lt;code&gt;knex&lt;/code&gt;
library in a unit test file and reports it as a linter error.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;no-db-unit-tests&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;create&lt;/span&gt;: &lt;span class="kt"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;ImportDeclaration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getFilename&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;specifiers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;specifiers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;knex&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;unit.spec&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sb"&gt;`Do not use &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; in unit test file &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fileName&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Where's My Flying Car?</title><link>https://notestoself.dev/reading/where-s-my-flying-car/</link><pubDate>Tue, 01 Dec 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/where-s-my-flying-car/</guid><description/></item><item><title>Jest mock error "ReferenceError Cannot access before initialization"</title><link>https://notestoself.dev/posts/jest-mock-reference-error-cannot-access-before-initialization/</link><pubDate>Wed, 18 Nov 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/jest-mock-reference-error-cannot-access-before-initialization/</guid><description>&lt;p&gt;When using Jest mocks, it&amp;rsquo;s easy to hit this error:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ReferenceError: Cannot access &amp;#39;...&amp;#39; before initialization
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is a bit vague and difficult to figure out if you don&amp;rsquo;t know what the issue
is.&lt;/p&gt;
&lt;p&gt;This error is due to the way
&lt;a href="https://github.com/kentcdodds/how-jest-mocking-works"&gt;Jest hoists mocks to the top&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For example if you have this in a test file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fooServiceFooFunction&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;@foo-provider/some-library&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;fooService&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;foobar&lt;/span&gt;: &lt;span class="kt"&gt;fooServiceFooFunction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It looks like it should work fine as the &lt;code&gt;fooServiceFooFunction&lt;/code&gt; mock function
is initialised before &lt;code&gt;jest.mock()&lt;/code&gt; is called to apply the mock.&lt;/p&gt;</description></item><item><title>Ask your interview candidates to ask you an algorithm question in return</title><link>https://notestoself.dev/posts/interview-candidate-should-ask-interviewer-algorithm-question/</link><pubDate>Thu, 12 Nov 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/interview-candidate-should-ask-interviewer-algorithm-question/</guid><description>&lt;p&gt;Here&amp;rsquo;s a potential idea to improve the technical interviews that you run: if you
use algorithm questions in interviews, ask candidates to ask you an algorithm
question as well.&lt;/p&gt;
&lt;p&gt;I think it could be beneficial to tell candidates that they should prepare an
algorithm question to pose to their interviewer and work through together in
the same way. This could have a similar vibe to the general questions at the
end of the interview, when we ask candidates if they have any questions for
us.&lt;/p&gt;</description></item><item><title>Uploading Etsy listing images in PHP with Oauth</title><link>https://notestoself.dev/posts/etsy-api-php-oauth-listing-image-upload/</link><pubDate>Fri, 23 Oct 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/etsy-api-php-oauth-listing-image-upload/</guid><description>&lt;p&gt;After figuring out how to
&lt;a href="https://notestoself.dev/posts/etsy-api-listing-image-upload-python/"&gt;upload Etsy listing images in Python&lt;/a&gt;
for one project, I needed to do the same thing in PHP for another. Despite Etsy
itself being implemented in PHP (as far as I know), and the examples in their
documentation being in PHP, this turned out to be tricky.&lt;/p&gt;
&lt;p&gt;First up, the example for uploading listing images in
&lt;a href="https://www.etsy.com/developers/documentation/getting_started/images"&gt;Etsy&amp;rsquo;s documentation&lt;/a&gt;
is plain wrong. It doesn&amp;rsquo;t work. Don&amp;rsquo;t waste any more time trying to get that
example to work.&lt;/p&gt;</description></item><item><title>Easy encrypted git with gcrypt</title><link>https://notestoself.dev/posts/easy-encrypted-git-gcrypt-gnupg/</link><pubDate>Sun, 23 Aug 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/easy-encrypted-git-gcrypt-gnupg/</guid><description>&lt;p&gt;I&amp;rsquo;ve been looking at options for keeping personal notes and diary entries stored
securely and synced across devices. An encrypted Git repo in Github seems like a
good potential option for this.&lt;/p&gt;
&lt;p&gt;It turns out this is pretty straightforward to achieve using
&lt;a href="https://github.com/spwhitton/git-remote-gcrypt"&gt;git-remote-gcrypt&lt;/a&gt;. This sets
up a Git remote that is encrypted while the local repository is kept in
plaintext and used normally.&lt;/p&gt;
&lt;p&gt;Check if you have a GPG key already:&lt;/p&gt;</description></item><item><title>Refactoring Terraform state into separate files in GCP GCS</title><link>https://notestoself.dev/posts/refactor-terraform-state-files-gcp-gcs/</link><pubDate>Sun, 23 Aug 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/refactor-terraform-state-files-gcp-gcs/</guid><description>&lt;p&gt;Recently I needed to refactor some Terraform configuration, which had grown into
quite a large single state file, into smaller separate directories with their
own state files.&lt;/p&gt;
&lt;p&gt;This is a little bit tricky but the Terraform CLI tool helps a lot.&lt;/p&gt;
&lt;p&gt;The first thing is to get the existing single state file JSON:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;terraform state pull &lt;span class="p"&gt;|&lt;/span&gt; tee terraform.tfstate
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This should work for any storage backend.&lt;/p&gt;
&lt;p&gt;Alternatively you can use &lt;code&gt;gsutil&lt;/code&gt; to copy the Terraform state file from a GCS
bucket:&lt;/p&gt;</description></item><item><title>The Great Successor</title><link>https://notestoself.dev/reading/the-great-successor/</link><pubDate>Sat, 01 Aug 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-great-successor/</guid><description/></item><item><title>Raw HTML shortcode in Hugo</title><link>https://notestoself.dev/posts/raw-html-shortcode-hugo/</link><pubDate>Tue, 28 Jul 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/raw-html-shortcode-hugo/</guid><description>&lt;p&gt;Here&amp;rsquo;s a little Hugo shortcode for including raw HTML within your Markdown
content.&lt;/p&gt;
&lt;p&gt;Just create a shortcode file at e.g. &lt;code&gt;layouts/shortcodes/rawhtml.html&lt;/code&gt; with this
content:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{.Inner}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now in your Markdown content you can include whatever raw HTML you want, e.g.:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{&amp;lt; rawhtml &amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;lt;button class=&amp;#34;buttonFromMarkdown&amp;#34;&amp;gt;Click Me!&amp;lt;/button&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;lt;script&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; document.querySelector(&amp;#34;.buttonFromMarkdown&amp;#34;).onclick = function (event) {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; console.log(&amp;#34;Clickety click&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; event.target.style = &amp;#34;background-color: &lt;span class="ni"&gt;#77dd77&lt;/span&gt;&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; };
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;lt;/script&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;lt;style&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; .buttonFromMarkdown {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; padding: 1em;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; cursor: pointer;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;lt;/style&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{&amp;lt; /rawhtml &amp;gt;}}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Which results in:&lt;/p&gt;</description></item><item><title>How I get more benefit from technical reading with Anki</title><link>https://notestoself.dev/posts/how-benefit-technical-reading-anki/</link><pubDate>Sun, 26 Jul 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/how-benefit-technical-reading-anki/</guid><description>&lt;p&gt;&lt;a href="https://apps.ankiweb.net/"&gt;Anki&lt;/a&gt; is one of my favourite pieces of software
ever, and it&amp;rsquo;s great to see that it seems to be getting more and more attention
as a valuable tool.&lt;/p&gt;
&lt;p&gt;I often do a kind of learning-and-note-taking exercise with Anki when reading
technical content, which I&amp;rsquo;m writing up here.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not complicated or dogmatic, and the current version boils down to three
tactics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cloze deletion for easy conversion of text into notes.&lt;/li&gt;
&lt;li&gt;Noting short code samples with a dedicated Code note-type.&lt;/li&gt;
&lt;li&gt;Use screenshots to turn anything into simple memory prompt notes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&amp;rsquo;s how each of those works for me. I&amp;rsquo;m using the article &lt;a href="https://hakibenita.com/sql-tricks-application-dba"&gt;&amp;ldquo;Some SQL Tricks
of an Application DBA&amp;rdquo;&lt;/a&gt; as an
example technical text that I recently went through with these note-taking
approaches.&lt;/p&gt;</description></item><item><title>Pivot joining 19 million lines of CSV with unix tools</title><link>https://notestoself.dev/posts/pivot-join-19-million-lines-csv-unix-tools/</link><pubDate>Thu, 11 Jun 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/pivot-join-19-million-lines-csv-unix-tools/</guid><description>&lt;p&gt;I&amp;rsquo;m participating in
&lt;a href="https://blog.tatoeba.org/2020/05/kodoeba-1-participants.html"&gt;Kodoeba&lt;/a&gt;, an open
hackathon around the &lt;a href="https://tatoeba.org/"&gt;Tatoeba&lt;/a&gt; example sentence project.&lt;/p&gt;
&lt;p&gt;My plan is to incorporate Mandarin-English example sentence pairs from Tatoeba
into the &lt;a href="https://www.chineseboost.com/chinese-example-sentences"&gt;Chinese example sentence
search&lt;/a&gt; on Chinese
Boost, mainly to allow searching them via pinyin with or without tone-marks.&lt;/p&gt;
&lt;p&gt;The first challenge was actually to extract the Mandarin-English example
sentence pairs from &lt;a href="https://tatoeba.org/eng/downloads"&gt;Tatoeba&amp;rsquo;s data files&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The data is structured in three separate TSV files (tab-separated values,
equivalent to CSV):&lt;/p&gt;</description></item><item><title>Productivity with random numbers</title><link>https://notestoself.dev/posts/productivity-random-numbers/</link><pubDate>Mon, 01 Jun 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/productivity-random-numbers/</guid><description>&lt;p&gt;One little productivity trick I use is to select tasks or work with a random
number. The simplest form is numbering a list of things you could do, then
selecting one randomly.&lt;/p&gt;
&lt;p&gt;This can be applied to various things you might want to work on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Items on a todo list.&lt;/li&gt;
&lt;li&gt;Chapters or pages in a textbook.&lt;/li&gt;
&lt;li&gt;Tickets in a work-tracking system.&lt;/li&gt;
&lt;li&gt;A list of projects, with sub-lists of work items.&lt;/li&gt;
&lt;li&gt;Options in a decision you&amp;rsquo;re stalling on.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The random number generator on &lt;a href="https://www.random.org/"&gt;random.org&lt;/a&gt; is quite
nice for this.&lt;/p&gt;</description></item><item><title>Simplicity resistance</title><link>https://notestoself.dev/posts/simplicity-resistance/</link><pubDate>Mon, 01 Jun 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/simplicity-resistance/</guid><description>&lt;p&gt;Here&amp;rsquo;s another term that ties in with the ideas in &lt;a href="https://notestoself.dev/posts/boring-considered-beneficial/"&gt;&amp;ldquo;boring considered
beneficial&amp;rdquo;&lt;/a&gt;, &lt;a href="https://notestoself.dev/posts/software-engineers-investment-managers-fee-justification/"&gt;&amp;ldquo;fee
justification&amp;rdquo;&lt;/a&gt; and
&lt;a href="https://notestoself.dev/posts/problem-maximisation/"&gt;&amp;ldquo;problem maximisation&amp;rdquo;&lt;/a&gt;: simplicity
resistance.&lt;/p&gt;
&lt;p&gt;When something is too easy, we dislike the sensation, and try to make it more
complicated in order to soothe that. This is a kind of cognitive dissonance
around finding solutions to problems. We think &amp;ldquo;it can&amp;rsquo;t be that easy&amp;rdquo;, &amp;ldquo;it&amp;rsquo;s
too good to be true&amp;rdquo; and so on, and start looking for the more complicated
solution that the problem at hand surely justifies.&lt;/p&gt;</description></item><item><title>Deploying a Hugo site with Github Actions, S3, Cloudfront and AWS IAM</title><link>https://notestoself.dev/posts/deploy-hugo-site-github-actions-s3-cloudfront-aws-iam/</link><pubDate>Thu, 28 May 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/deploy-hugo-site-github-actions-s3-cloudfront-aws-iam/</guid><description>&lt;p&gt;GitHub Actions are a nice way to get &amp;ldquo;push to deploy&amp;rdquo; behaviour for a static
site. Here&amp;rsquo;s a quick run-through of how I have it working for Hugo sites being
deployed to AWS.&lt;/p&gt;
&lt;p&gt;This assumes you have created an S3 bucket and Cloudfront distribution for the
site, and have pointed the domain name to the Cloudfront distribution.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s best to have an isolated IAM user for deploying with minimal permissions.
Create a new policy in AWS IAM, and give it minimal permissions for deploying
the site.&lt;/p&gt;</description></item><item><title>Does your code read like your conversations?</title><link>https://notestoself.dev/posts/code-like-conversations/</link><pubDate>Thu, 28 May 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/code-like-conversations/</guid><description>&lt;p&gt;This is a potential rule-of-thumb subjective metric for assessing how well
you&amp;rsquo;re directing your software development resources:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Does your code read like your conversations?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;What this means is, does the code you&amp;rsquo;re writing overlap closely with the
conversations you have about the product and the problems it solves?&lt;/p&gt;
&lt;p&gt;For example, if you talk about &amp;ldquo;free month of foobar with the XYZ subscription&amp;rdquo;,
then it&amp;rsquo;s probably a good sign if those words appear a lot in the code.
Hopefully there are classes and functions that overlap with that.&lt;/p&gt;</description></item><item><title>A lack of correlation does imply a lack of causation</title><link>https://notestoself.dev/posts/lack-correlation-no-causation/</link><pubDate>Sat, 23 May 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/lack-correlation-no-causation/</guid><description>&lt;p&gt;Correlation does not imply causation, but a lack of correlation does imply a
lack of causation.&lt;/p&gt;
&lt;p&gt;The famous maxim that &amp;ldquo;correlation does not imply causation&amp;rdquo; seems to get
over-interpreted into some kind of suggestion that correlation and causation can
never be related in any way.&lt;/p&gt;
&lt;p&gt;One aspect of that is that a &lt;em&gt;lack&lt;/em&gt; of correlation &lt;em&gt;does&lt;/em&gt; imply that there is no
causation effect going on. If there was a causative relationship between two
things, we would expect to see correlation.&lt;/p&gt;</description></item><item><title>Engineer pain vs user pain</title><link>https://notestoself.dev/posts/engineer-pain-vs-user-pain/</link><pubDate>Mon, 18 May 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/engineer-pain-vs-user-pain/</guid><description>&lt;p&gt;A lot of software engineering teams talk about &amp;ldquo;pain&amp;rdquo; on a regular basis.&lt;/p&gt;
&lt;p&gt;Usually this refers to difficulties around technical debt, design decisions,
refactoring and so on. It rarely seems to refer to issues that affect users or
the rest of the business.&lt;/p&gt;
&lt;p&gt;For example, moving to microservices is often touted as a way to &amp;ldquo;reduce pain&amp;rdquo;.
Perhaps we can reduce pain by putting HTTP calls in between different parts of
the system, but it&amp;rsquo;s rare that this improves things for users in any way.&lt;/p&gt;</description></item><item><title>Crystal ball development</title><link>https://notestoself.dev/posts/crystal-ball-development/</link><pubDate>Sat, 16 May 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/crystal-ball-development/</guid><description>&lt;p&gt;There&amp;rsquo;s a well known software development maxim called
&lt;a href="https://en.wikipedia.org/wiki/You_aren%27t_gonna_need_it"&gt;YAGNI&lt;/a&gt;: &amp;ldquo;You Ain&amp;rsquo;t
Gonna Need It&amp;rdquo;. YAGNI is a general rule for deciding whether or not to implement
something or add complexity. It&amp;rsquo;s designed to give some resistance against the
temptation to spuriously add features or try to solve hypothetical future
problems.&lt;/p&gt;
&lt;p&gt;Applying a bit of &lt;a href="https://fs.blog/2013/10/inversion/"&gt;inversion&lt;/a&gt; to YAGNI, you
get something that might be called &amp;ldquo;crystal ball development&amp;rdquo;. This describes
the attempt to make magic predictions about the future and apply them to
software development. As with a crystal ball in popular culture, the prediction
is an illusion that leads you astray.&lt;/p&gt;</description></item><item><title>Problem maximisation</title><link>https://notestoself.dev/posts/problem-maximisation/</link><pubDate>Fri, 08 May 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/problem-maximisation/</guid><description>&lt;p&gt;A common metaphor about the potential dangers of AI is called &lt;a href="https://wiki.lesswrong.com/wiki/Paperclip_maximizer"&gt;&amp;ldquo;paperclip
maximisation&amp;rdquo;&lt;/a&gt;. The idea
is that an AI tasked with producing paperclips could figure out that it can
produce more paperclips by e.g. taking over the entire galaxy to turn as much
matter as possible into paperclips.&lt;/p&gt;
&lt;p&gt;Sometimes I wonder if software engineers are prone to a similar issue. We
believe we&amp;rsquo;re tasked with solving as many problems as possible, and realise that
if we increase the number of problems, then we can increase the number of
solutions.&lt;/p&gt;</description></item><item><title>Python script to automate static Hugo site post images</title><link>https://notestoself.dev/posts/python-script-automate-static-hugo-site-post-images/</link><pubDate>Sun, 03 May 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/python-script-automate-static-hugo-site-post-images/</guid><description>&lt;p&gt;It&amp;rsquo;s good to have a general post image on content sites, as it gives more of an
identity to each page, and works well for
&lt;a href="https://notestoself.dev/posts/easy-social-media-images-and-markup-with-hugo/"&gt;social media sharing images&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;While NotesToSelf.dev doesn&amp;rsquo;t tend to have images, my &lt;a href="https://www.chineseboost.com/"&gt;Chinese
learning&lt;/a&gt; site and &lt;a href="https://fuuu.dev/"&gt;satirical tech
news&lt;/a&gt; site have an image for each post.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://unsplash.com/"&gt;Unsplash&lt;/a&gt; is a nice source of general royalty-free
images that you can use for this if you give attribution.&lt;/p&gt;
&lt;p&gt;When using Unsplash, I set up the url to the Unsplash page (not the image
itself) in the frontmatter of each post:&lt;/p&gt;</description></item><item><title>Easy social media images and markup with Hugo</title><link>https://notestoself.dev/posts/easy-social-media-images-and-markup-with-hugo/</link><pubDate>Sun, 19 Apr 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/easy-social-media-images-and-markup-with-hugo/</guid><description>&lt;p&gt;&lt;a href="https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/abouts-cards"&gt;Twitter
Cards&lt;/a&gt;
and &lt;a href="https://ogp.me/"&gt;Facebook&amp;rsquo;s OpenGraph&lt;/a&gt; provide semi-standardised markup for
describing web content to social media and other aggregators.&lt;/p&gt;
&lt;p&gt;This can be quite handy for blogs and other text content, as you can provide
default information when your content is shared. This is a nice way to make sure
your posts get a reasonable cover image when shared to someone&amp;rsquo;s social
timeline.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the Hugo / Go HTML template I&amp;rsquo;m currently using here to add some basic
sharing markup to each post as part of the HTML head:&lt;/p&gt;</description></item><item><title>Abstraction castles</title><link>https://notestoself.dev/posts/abstraction-castles/</link><pubDate>Sat, 18 Apr 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/abstraction-castles/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;&amp;hellip;some service maintained by one person that&amp;rsquo;s built their little
impregnable abstraction castle because nobody else had to work on it so they
could just yak-shave and bike-shed with their self.&amp;rdquo; [sic]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://news.ycombinator.com/item?id=16201126"&gt;https://news.ycombinator.com/item?id=16201126&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;That Hacker News comment struck a chord with me, as I think the phrase
&amp;ldquo;impregnable abstraction castle&amp;rdquo; perfectly captures something that is commonly
encountered in software.&lt;/p&gt;
&lt;p&gt;An abstraction castle appears as follows. An area of a codebase seems to develop
into a fortification that is resistant to change, apparently designed to defend
against all envisioned future scenarios and to keep out every encroachment of
the outside world that the designers could imagine.&lt;/p&gt;</description></item><item><title>Using Laravel signed routes to improve order confirmation security</title><link>https://notestoself.dev/posts/laravel-signed-routes-order-confirmation-security/</link><pubDate>Thu, 16 Apr 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/laravel-signed-routes-order-confirmation-security/</guid><description>&lt;p&gt;The order confirmation page on our &lt;a href="https://www.poprobincards.co.uk/" title="Pop Robin Cards: 3D pop up cards"&gt;3D cards
shop&lt;/a&gt; is
shown to a customer after they complete checkout, and is also linked to in the
confirmation and dispatch emails that they receive.&lt;/p&gt;
&lt;p&gt;Importantly, this page is not authenticated; we don&amp;rsquo;t want a heavy sign up /
sign in process for customers as that tends to be annoying on other ecommerce
systems. I just want to buy some pop-up cards &amp;ndash; why do I need to create an
account and remember a password etc?&lt;/p&gt;</description></item><item><title>Specify city timezones, not timezone offsets</title><link>https://notestoself.dev/posts/specify-city-timezones-not-offsets/</link><pubDate>Sat, 11 Apr 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/specify-city-timezones-not-offsets/</guid><description>&lt;p&gt;Dates and times are &lt;a href="https://gist.github.com/timvisee/fcda9bbdff88d45cc9061606b4b923ca"&gt;notoriously
difficult&lt;/a&gt; to
get right in software. Many of the difficulties come from timezones and how
unintuitive they are to think about. This is made worse by timezone offsets that
change during the year for the same place, such as British Summer Time.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s easier to get timezones right when you specify a region/city timezone, such
as &lt;code&gt;Europe/London&lt;/code&gt;. Trying to think about and specify timezone offsets such as
&lt;code&gt;+01:00&lt;/code&gt; is more likely to go wrong.&lt;/p&gt;</description></item><item><title>A simple, readable, meaningful test style with Jest</title><link>https://notestoself.dev/posts/simple-readable-meaningful-jest-test-style/</link><pubDate>Mon, 06 Apr 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/simple-readable-meaningful-jest-test-style/</guid><description>&lt;p&gt;I usually write tests with a
&lt;a href="https://notestoself.dev/posts/start-with-given-when-then-tests/"&gt;Given, When, Then&lt;/a&gt; structure to
keep the test focused on documenting and confirming the important behaviour of
whatever is being tested. Simple comments are good for this and provide an
easy-to-follow visual structure:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;some defined behaviour&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Given the fandango is combobulated;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// When we discombobulate the fandango;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Then the fandango should be discombobulated.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s important to keep tests clear and readable. Reducing the number of lines
under each step and making each line as meaningful as possible are the main ways
to do that. You can use
&lt;a href="https://notestoself.dev/posts/flexible-test-model-factories-in-typescript/"&gt;test factories&lt;/a&gt; and other
extractions to reduce line-count and make test code more meaningful.&lt;/p&gt;</description></item><item><title>Fastmail is a nice multi-domain custom email service</title><link>https://notestoself.dev/posts/fastmail-multi-domain-custom-email-service/</link><pubDate>Sun, 05 Apr 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/fastmail-multi-domain-custom-email-service/</guid><description>&lt;p&gt;I run a few &lt;a href="https://notestoself.dev/work/"&gt;side projects&lt;/a&gt; on various domains, and often want to be
able to send and receive email on those domains. Over the years I&amp;rsquo;ve used a few
different solutions for this: Gmail aliases, &lt;a href="https://www.mailgun.com/"&gt;Mailgun&lt;/a&gt;
just for automated sending, AWS SES just for automated sending and
&lt;a href="https://aws.amazon.com/en/workmail/"&gt;AWS Workmail&lt;/a&gt; for manual sending and
receiving.&lt;/p&gt;
&lt;p&gt;AWS Workmail has been fine, but ends up being a bit pricey for this kind of
usage at $4 USD / user / month. You have to have at least one &amp;ldquo;user&amp;rdquo; per domain,
so with multiple domains this adds up quite quickly and gets prohibitive for
random side projects and experiments.&lt;/p&gt;</description></item><item><title>Random element choice in a Hugo HTML template</title><link>https://notestoself.dev/posts/random-choice-hugo-template/</link><pubDate>Fri, 03 Apr 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/random-choice-hugo-template/</guid><description>&lt;p&gt;I run a couple of sites related to my Chinese studies: an &lt;a href="https://eastasiastudent.net/"&gt;East Asian Studies
blog&lt;/a&gt;, and a &lt;a href="https://www.chineseboost.com/"&gt;Chinese learning
resource&lt;/a&gt; site.&lt;/p&gt;
&lt;p&gt;The content is all free so I have some simple affiliate image links on the
pages to cover the bills. These are just straight forward &lt;code&gt;&amp;lt;a&amp;gt;&amp;lt;img&amp;gt;&amp;lt;/a&amp;gt;&lt;/code&gt;
elements with no tracking or anything obnoxious at all. Even then, I don&amp;rsquo;t want
to have too many adverts on a single page as it would still be a bit much.&lt;/p&gt;</description></item><item><title>(Cross-post) End to end serverless testing with the Driver Pattern</title><link>https://notestoself.dev/posts/cross-post-e2e-serverless-testing-driver-pattern/</link><pubDate>Thu, 02 Apr 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/cross-post-e2e-serverless-testing-driver-pattern/</guid><description>&lt;p&gt;(Cross-posted from
&lt;a href="https://blog.freetrade.io/end-to-end-serverless-testing-with-the-driver-pattern-bcf2d22bf73a"&gt;https://blog.freetrade.io/end-to-end-serverless-testing-with-the-driver-pattern-bcf2d22bf73a&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;In September 2019, Freetrade’s test code consisted of a large number of fast
unit tests and a smaller number of end-to-end API tests. These make real HTTP
requests against the platform and assert on the results.&lt;/p&gt;
&lt;p&gt;The unit tests confirm that individual classes are behaving correctly and
improve the design of interfaces, while the API tests confirm that higher-level
business logic is working correctly. This is not a bad combination and at the
time already gave us decent test coverage of a large proportion of our platform.&lt;/p&gt;</description></item><item><title>Choosing dates and times in tests</title><link>https://notestoself.dev/posts/choosing-dates-times-tests/</link><pubDate>Mon, 30 Mar 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/choosing-dates-times-tests/</guid><description>&lt;p&gt;A little note to avoid bugs in tests, or missing bugs: don&amp;rsquo;t use the current
date or time when writing unit tests.&lt;/p&gt;
&lt;p&gt;When making up data for tests, it&amp;rsquo;s common to just use today&amp;rsquo;s date as it&amp;rsquo;s easy
to think up. This can catch you out when there is actually some hidden
dependency on the real time and your test starts failing the next day, month or
year. The test is currently passing by coincidence, which is hard to distinguish
from a real pass if you&amp;rsquo;re not careful.&lt;/p&gt;</description></item><item><title>Do software engineers have the same "fee justification" problem as investment managers?</title><link>https://notestoself.dev/posts/software-engineers-investment-managers-fee-justification/</link><pubDate>Thu, 12 Mar 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/software-engineers-investment-managers-fee-justification/</guid><description>&lt;p&gt;It&amp;rsquo;s widely known (although somehow still controversial) that investment
managers who try to manually pick stocks cannot beat the market the vast
majority of the time:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.cnbc.com/2019/03/15/active-fund-managers-trail-the-sp-500-for-the-ninth-year-in-a-row-in-triumph-for-indexing.html"&gt;&amp;ldquo;Active fund managers trail the S&amp;amp;P 500 for the ninth year in a row in triumph for indexing&amp;rdquo; | CNBC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fortune.com/2017/04/13/stock-indexes-beat-mutual-funds/"&gt;&amp;ldquo;Stock-Picking Fund Managers Are Even Worse Than We Thought At Beating the Market&amp;rdquo; | Fortune&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.aei.org/carpe-diem/more-evidence-that-its-very-hard-to-beat-the-market-over-time-95-of-financial-professionals-cant-do-it/"&gt;&amp;ldquo;More evidence that it’s very hard to ‘beat the market’ over time, 95% of finance professionals can’t do it&amp;rdquo; | AEI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.thebalance.com/what-robo-advisors-do-better-than-financial-advisors-4154903"&gt;&amp;ldquo;Your robo-advisor probably won’t be able to beat the stock market. But then again, neither will your human advisor&amp;rdquo; | The Balance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That is even without the fees that active investment managers charge, which are
usually 1% or more (which is horrendous long-term when subtracted from compound
interest).&lt;/p&gt;</description></item><item><title>Decision fatigue and job interviewing</title><link>https://notestoself.dev/posts/decision-fatigue-and-job-interviewing/</link><pubDate>Mon, 09 Mar 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/decision-fatigue-and-job-interviewing/</guid><description>&lt;p&gt;I was reading about &lt;a href="https://en.wikipedia.org/wiki/Decision_fatigue"&gt;decision
fatigue&lt;/a&gt; and wondering if it
accounts for more major events than we give it credit for. The idea is at least
spreading a little bit in certain circles, but doesn&amp;rsquo;t get the wider attention
it should.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;One research study found that the decisions judges make are strongly
influenced by how long it has been since their last break. &amp;ldquo;We find that the
percentage of favorable rulings drops gradually from ≈65% to nearly zero
within each decision session and returns abruptly to ≈65% after a break.&amp;rdquo;&amp;rdquo;&lt;/p&gt;</description></item><item><title>SQL Puzzle 2: Absentees</title><link>https://notestoself.dev/posts/sql-puzzles-2-absentees/</link><pubDate>Mon, 02 Mar 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/sql-puzzles-2-absentees/</guid><description>&lt;p&gt;The second puzzle in Joe Celko&amp;rsquo;s &lt;em&gt;SQL Puzzles&lt;/em&gt; is about tracking employee
absenteeism.&lt;/p&gt;
&lt;p&gt;The initial table structure is like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-postgresql" data-lang="postgresql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;personnel&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="n"&gt;emp_id&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;excuse_list&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="n"&gt;reason_code&lt;/span&gt; &lt;span class="nb"&gt;varchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;absenteeism&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="n"&gt;emp_id&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;personnel&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emp_id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="n"&gt;absent_date&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="n"&gt;reason_code&lt;/span&gt; &lt;span class="nb"&gt;varchar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;40&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;excuse_list&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reason_code&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="n"&gt;severity_points&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;CONSTRAINT&lt;/span&gt; &lt;span class="n"&gt;severity_points_range&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;severity_points&lt;/span&gt; &lt;span class="k"&gt;BETWEEN&lt;/span&gt; &lt;span class="mf"&gt;1&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="mf"&gt;4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emp_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;absent_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;There are already some nice properties to notice in this schema:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All columns are &lt;code&gt;NOT NULL&lt;/code&gt;, which is ideal.&lt;/li&gt;
&lt;li&gt;The list of valid absence reasons is extracted into an explicit table that
will be easier to work with than an enum column within the absenteeism table.&lt;/li&gt;
&lt;li&gt;There&amp;rsquo;s a simple but useful check constraint to ensure &lt;code&gt;severity_points&lt;/code&gt; is
valid (this could also have been a foreign key to a separate table).&lt;/li&gt;
&lt;li&gt;There&amp;rsquo;s a natural primary key of &lt;code&gt;(emp_id, absent_date)&lt;/code&gt; on the &lt;code&gt;absenteeism&lt;/code&gt;
table. This will improve consistency and let the database optimise queries
more effectively.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal in this SQL puzzle is to make (kind of nasty!) decisions about the
consequences of employee absenteeism using this schema, with fiddly rules around
how to count and rate absence:&lt;/p&gt;</description></item><item><title>You are not doing Test Driven Development</title><link>https://notestoself.dev/posts/you-are-not-doing-test-driven-development/</link><pubDate>Sun, 01 Mar 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/you-are-not-doing-test-driven-development/</guid><description>&lt;p&gt;Test Driven Development can be a business process, not just a development
workflow. It can start much earlier in the software engineering process than
when implementation starts and code is first written.&lt;/p&gt;
&lt;p&gt;Quite a few engineering teams confuse TDD for doing any testing at all, but even
teams that know TDD is a stronger process than that still limit its potential by
confining it to an implementation workflow detail that is trivial at the level
of the business or software team.&lt;/p&gt;</description></item><item><title>Rendering HTML email version of articles with Hugo</title><link>https://notestoself.dev/posts/rendering-html-email-version-of-articles-with-hugo/</link><pubDate>Wed, 26 Feb 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/rendering-html-email-version-of-articles-with-hugo/</guid><description>&lt;p&gt;I recently &lt;a href="https://notestoself.dev/posts/setting-up-a-mailcoach-app-on-aws-with-laravel-forge/"&gt;started using Mailcoach&lt;/a&gt;,
mainly for sending emails to the subscriber list for &lt;a href="https://www.chineseboost.com/" title="Learn Chinese"&gt;Chinese Boost&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Mailcoach&amp;rsquo;s HTML email templating abilities are &lt;a href="https://mailcoach.app/docs/app/templates/overview"&gt;pretty
basic&lt;/a&gt;, but that&amp;rsquo;s fine. I&amp;rsquo;m
happy to use simple HTML emails, and found this &lt;a href="https://github.com/leemunroe/responsive-html-email-template"&gt;HTML email
template&lt;/a&gt; that
looks like it will do the job nicely.&lt;/p&gt;
&lt;p&gt;As the content on Chinese Boost is managed with Hugo, it would be nice to have
Hugo also be responsible for generating HTML email content for each article that
can then just be copy-pasted into Mailcoach for sending to subscribers. Hugo has
an &lt;a href="https://gohugo.io/templates/output-formats/"&gt;output formats&lt;/a&gt; feature that
lets you do exactly that.&lt;/p&gt;</description></item><item><title>SQL Puzzle 1: Fiscal Year Tables</title><link>https://notestoself.dev/posts/sql-puzzles-1-fiscal-year-tables/</link><pubDate>Wed, 26 Feb 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/sql-puzzles-1-fiscal-year-tables/</guid><description>&lt;p&gt;The first puzzle in &lt;em&gt;SQL Puzzles&lt;/em&gt; is about adding strong constraints to ensure
correctness of data in a table that looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-postgresql" data-lang="postgresql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;fiscal_years&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="n"&gt;fiscal_year&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="n"&gt;start_date&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="n"&gt;end_date&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Making all the columns &lt;code&gt;NOT NULL&lt;/code&gt; is a good default first step as it avoids a
large source of errors.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-postgresql" data-lang="postgresql"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;fiscal_years&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="n"&gt;fiscal_year&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="n"&gt;start_date&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="n"&gt;end_date&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We can prevent other errors by adding &lt;code&gt;UNIQUE&lt;/code&gt; constraints to each of the three
columns, as we don&amp;rsquo;t want any shared values across rows for them: each fiscal
year should only be described once, and no fiscal year can share a start date or
end date with another.&lt;/p&gt;</description></item><item><title>Setting up a Mailcoach app on AWS with Laravel Forge</title><link>https://notestoself.dev/posts/setting-up-a-mailcoach-app-on-aws-with-laravel-forge/</link><pubDate>Sun, 23 Feb 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/setting-up-a-mailcoach-app-on-aws-with-laravel-forge/</guid><description>&lt;p&gt;I&amp;rsquo;ve been looking forward to the release of &lt;a href="https://mailcoach.app/"&gt;Mailcoach&lt;/a&gt;,
which is a self-hosted mailing list manager app. I have a few mailing lists,
most of which are tiny, but one of which has over 3000 subscribers. This puts it
over the threshold of expensive subscription options in MailChimp. MailChimp
wants over 70 USD / month to run this single list, whereas a Mailcoach license
is a one-off payment of 99 USD. Plus it seems like it would be interesting to
self-host the mailing list manager and learn a few things along the way.&lt;/p&gt;</description></item><item><title>Better alive than right</title><link>https://notestoself.dev/posts/better-alive-than-right/</link><pubDate>Wed, 19 Feb 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/better-alive-than-right/</guid><description>&lt;p&gt;&amp;ldquo;Better alive than right&amp;rdquo; is a kind of proverb I try to remind myself of when
cycling in London on a daily basis. A sizeable minority of drivers in London
have a disregard for people on bikes and break traffic rules in various ways
around them.&lt;/p&gt;
&lt;p&gt;A common one is drivers pulling out from a junction in front of someone on a
bike, which requires the person cycling to stop to avoid a collision, even
though the driver is at fault. The idea of &amp;ldquo;better alive than right&amp;rdquo; is to
focus on keeping yourself safe above all, even when it&amp;rsquo;s frustrating as the
driver is in the wrong in this situation.&lt;/p&gt;</description></item><item><title>Eating the recipe</title><link>https://notestoself.dev/posts/eating-the-recipe/</link><pubDate>Mon, 10 Feb 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/eating-the-recipe/</guid><description>&lt;p&gt;&amp;ldquo;Eating the recipe&amp;rdquo; is a term I&amp;rsquo;ve made up to try and describe the kind of
mistakes that get made by mixing up levels of abstraction.&lt;/p&gt;
&lt;p&gt;Humans do not struggle with the distinction between a recipe and the food that
the recipe describes how to make. We would not make the mistake of eating a
sheet of paper that describes how to make spaghetti, instead of eating the
spaghetti. We do, however, design and use systems that make this kind of mistake
a lot, or make it easy for humans to make this kind of mistake.&lt;/p&gt;</description></item><item><title>Personal bike shedding</title><link>https://notestoself.dev/posts/personal-bike-shedding/</link><pubDate>Sun, 09 Feb 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/personal-bike-shedding/</guid><description>&lt;p&gt;The concept of &lt;a href="https://en.wikipedia.org/wiki/Law_of_triviality"&gt;bike shedding&lt;/a&gt;
is one of my favourites, as once you&amp;rsquo;ve learnt about it, it won&amp;rsquo;t be long
before you see it in the wild at most organisations.&lt;/p&gt;
&lt;p&gt;The standard explanation (from Wikipedia), goes like this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Parkinson provides the example of a fictional committee whose job was to
approve the plans for a nuclear power plant spending the majority of its
time on discussions about relatively minor but easy-to-grasp issues, such as
what materials to use for the staff bike shed, while neglecting the proposed
design of the plant itself, which is far more important and a far more
difficult and complex task.&amp;rdquo;&lt;/p&gt;</description></item><item><title>Write code that is easy to delete</title><link>https://notestoself.dev/posts/write-code-that-is-easy-to-delete/</link><pubDate>Sat, 08 Feb 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/write-code-that-is-easy-to-delete/</guid><description>&lt;p&gt;It&amp;rsquo;s hard to write software that is easy to maintain and adapt to change.
There&amp;rsquo;s a multitude of maxims to try and tease out general solutions to this, or
at least approaches that can mitigate the problem.&lt;/p&gt;
&lt;p&gt;This point did the rounds on Hacker News and elsewhere:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Write code that is easy to delete, not easy to extend.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;ndash; &lt;a href="https://programmingisterrible.com/post/139222674273/write-code-that-is-easy-to-delete-not-easy-to"&gt;programmingisterrible&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is one of the better maxims for producing maintainable and adaptable
software. One of the most important aspects that distinguishes it is that it
acknowledges that &lt;em&gt;your code is probably bad&lt;/em&gt;. At some point, in some situation,
in someone&amp;rsquo;s perception, the code you are writing now is going to be bad and
difficult. The person that encounters that difficulty is also likely to be you.&lt;/p&gt;</description></item><item><title>Generating business name ideas with Bash and Linux</title><link>https://notestoself.dev/posts/generate-business-name-ideas-bash-linux/</link><pubDate>Tue, 04 Feb 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/generate-business-name-ideas-bash-linux/</guid><description>&lt;p&gt;I&amp;rsquo;m setting up a small side-business selling
&lt;a href="https://momentwallart.co.uk/"&gt;wall art prints&lt;/a&gt; in the UK, and needed to think of
a short, reasonably unique name.&lt;/p&gt;
&lt;p&gt;After Googling for word lists and name generators, I thought it would actually
work best to get Bash to generate a list of name ideas for me, keeping the words
to a limited length and randomising them to avoid fatigue when reading the list.&lt;/p&gt;
&lt;p&gt;The above can be done with this command on Ubuntu Linux:&lt;/p&gt;</description></item><item><title>Some easy ways to improve dev workflows</title><link>https://notestoself.dev/posts/dev-workflow-business-process-too/</link><pubDate>Mon, 03 Feb 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/dev-workflow-business-process-too/</guid><description>&lt;p&gt;Your dev workflow is a business process too.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a funny phenomenon in a lot of companies that hire software engineers,
where people draw a dividing line between &amp;ldquo;engineering&amp;rdquo; and &amp;ldquo;the business&amp;rdquo;. This
is even reinforced in a lot of software engineering books and other media, which
talk about &amp;ldquo;business people&amp;rdquo; and so on. I always think of engineering as very
much a part of this apparently separate &amp;ldquo;business&amp;rdquo;, but that&amp;rsquo;s at least a whole
other blog post.&lt;/p&gt;</description></item><item><title>Simple way to use ts-firebase-driver-testing with a Jest mock</title><link>https://notestoself.dev/posts/firebase-driver-testing-simple-jest-mock/</link><pubDate>Thu, 23 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/firebase-driver-testing-simple-jest-mock/</guid><description>&lt;p&gt;While working at Freetrade, I made the
&lt;a href="https://github.com/freetrade-io/ts-firebase-driver-testing"&gt;ts-firebase-driver-testing&lt;/a&gt;
library to allow rapid testing and debugging of Firebase applications (other
engineers have contributed to it since then).&lt;/p&gt;
&lt;p&gt;The initial plan with the library was to abstract out the Firebase SDK as a set
of plain interfaces, and then provide a single-process in-memory implementation
of those interfaces. This worked fine and it does let you swap out Firebase
when running tests, which was the main goal.&lt;/p&gt;</description></item><item><title>Extract interfaces, don't pre-plan them</title><link>https://notestoself.dev/posts/extract-interfaces-dont-pre-plan-them/</link><pubDate>Mon, 20 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/extract-interfaces-dont-pre-plan-them/</guid><description>&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Interface_segregation_principle"&gt;Interface Segration
Principle&lt;/a&gt; is one
of the more overlooked parts of &lt;a href="https://en.wikipedia.org/wiki/SOLID"&gt;SOLID&lt;/a&gt;. I
think this might be because it gets seen as a restatement of the &lt;a href="https://en.wikipedia.org/wiki/Single_responsibility_principle"&gt;Single
Responsibility
Principle&lt;/a&gt;, and
not as the separate idea that it actually is.&lt;/p&gt;
&lt;p&gt;Interface segration is about keeping interfaces lean and specific to the
smallest possible purpose, as suggested by the formal definition:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;No client should be forced to depend on methods it does not use.&amp;rdquo;&lt;/p&gt;</description></item><item><title>The code should be a sequence diagram too</title><link>https://notestoself.dev/posts/code-should-be-sequence-diagram/</link><pubDate>Sat, 18 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/code-should-be-sequence-diagram/</guid><description>&lt;p&gt;I love sequence diagrams and other kinds of visual documentation of a system,
especially ones that are committed in the repo alongside the application code.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://plantuml.com/"&gt;PlantUML&lt;/a&gt; is a nice tool for this as there&amp;rsquo;s a simple
mark-up language for creating and editing diagrams which sits nicely in a Git
repo. You can commit and review changes to diagrams as you do with the rest of
the code.&lt;/p&gt;
&lt;p&gt;Diagrams are a kind of comment, though, and so really they should only
supplement the code and not be a substitute for readability or clarity of the
application code itself.&lt;/p&gt;</description></item><item><title>Python script to convert an Etsy sales export CSV into a Xero import CSV</title><link>https://notestoself.dev/posts/etsy-sales-csv-xero-import-python-script/</link><pubDate>Sat, 11 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/etsy-sales-csv-xero-import-python-script/</guid><description>&lt;p&gt;We handle the accounting for our &lt;a href="https://www.poprobincards.co.uk/"&gt;pop out cards
shop&lt;/a&gt; using Xero. There are convenient
pre-built integrations that handle Stripe and PayPal data in Xero, but nothing
for Etsy, so I had to do a little Python script to generate a Xero import CSV
file from the Etsy orders export CSV.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the script for future reference:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="ch"&gt;#!/usr/bin/python3&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# https://www.etsy.com/uk/your/shops/YourShopName/download&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Sold Orders&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# e.g. EtsySoldOrders2020.csv&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;argparse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;csv&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;io&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TextIOWrapper&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;SALES_ACCOUNT_CODE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;200&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;NOW&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;XERO_FIELDS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*ContactName&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;EmailAddress&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;POAddressLine1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;POAddressLine2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;POAddressLine3&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;POAddressLine4&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;POCity&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;PORegion&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;POPostalCode&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;POCountry&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*InvoiceNumber&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Reference&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*InvoiceDate&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*DueDate&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Total&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;InventoryItemCode&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*Description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*Quantity&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*UnitAmount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Discount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*AccountCode&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*TaxType&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;TaxAmount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;TrackingName1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;TrackingOption1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;TrackingName2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;TrackingOption2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Currency&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;BrandingTheme&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;convert_etsy_to_xero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;etsy_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TextIOWrapper&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;output_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;etsy_xero_orders_&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;etsy_file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;w&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newline&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;output_file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;etsy_reader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DictReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;etsy_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;delimiter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quotechar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;xero_writer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;csv&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DictWriter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output_file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;XERO_FIELDS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;delimiter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;quotechar&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;xero_writer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writeheader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;etsy_order&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;etsy_reader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;xero_writer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;writerow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;etsy_order_to_xero_row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;etsy_order_to_xero_row&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*ContactName&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Full Name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;EmailAddress&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;POAddressLine1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Street 1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;POAddressLine2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Street 2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;POAddressLine3&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;POAddressLine4&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;POCity&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Delivery City&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;PORegion&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Delivery State&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;POPostalCode&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Delivery Zipcode&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;POCountry&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Delivery Country&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*InvoiceNumber&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Order ID&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Reference&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*InvoiceDate&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_date_to_xero_date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Sale Date&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*DueDate&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_date_to_xero_date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Sale Date&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Total&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Order total&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;InventoryItemCode&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*Description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Etsy Order &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Order ID&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*Quantity&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*UnitAmount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Order total&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Discount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*AccountCode&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SALES_ACCOUNT_CODE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;*TaxType&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Sales&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;TaxAmount&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;TrackingName1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;TrackingOption1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;TrackingName2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;TrackingOption2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;Currency&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Currency&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;BrandingTheme&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;etsy_date_to_xero_date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;etsy_date&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strptime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;etsy_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;%m/&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s2"&gt;/%y&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s2"&gt; %b %Y&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vm"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;__main__&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Convert an Etsy sales export CSV into a xero invoices CSV&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;etsy_file&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;EtsySoldOrders&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.csv&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;argparse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FileType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;r&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;encoding&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;help&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Path to Etsy sales export CSV file&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;args&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;parse_args&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;convert_etsy_to_xero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;etsy_file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;etsy_file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Be careful with Node file globbing vs bash globbing</title><link>https://notestoself.dev/posts/be-careful-with-node-file-globbing-vs-bash-globbing/</link><pubDate>Thu, 02 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/be-careful-with-node-file-globbing-vs-bash-globbing/</guid><description>&lt;p&gt;There&amp;rsquo;s a common gotcha when using commandline tools that have their own file
globbing, such as Prettier in Nodejs.&lt;/p&gt;
&lt;p&gt;This command is supposed to run prettier against all the &lt;code&gt;.ts&lt;/code&gt; files in all
folders under &lt;code&gt;src&lt;/code&gt; recursively;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;prettier --write src/**/*.ts
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;What actually happens though is that bash expands the &lt;code&gt;src/**/*.ts&lt;/code&gt; first, which
will only go one folder deep instead of the intended unlimited recursion.&lt;/p&gt;
&lt;p&gt;The fix is simply to quote the globbing express so that bash passes it to
prettier unscathed:&lt;/p&gt;</description></item><item><title>Atlas Shrugged</title><link>https://notestoself.dev/reading/atlas-shrugged/</link><pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/atlas-shrugged/</guid><description/></item><item><title>Conversations with Friends</title><link>https://notestoself.dev/reading/conversations-with-friends/</link><pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/conversations-with-friends/</guid><description/></item><item><title>Drink</title><link>https://notestoself.dev/reading/drink/</link><pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/drink/</guid><description/></item><item><title>How Fascism Works</title><link>https://notestoself.dev/reading/how-fascism-works/</link><pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/how-fascism-works/</guid><description/></item><item><title>If This is a Man · The Truce</title><link>https://notestoself.dev/reading/if-this-is-a-man-the-truce/</link><pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/if-this-is-a-man-the-truce/</guid><description/></item><item><title>My Brilliant Friend</title><link>https://notestoself.dev/reading/my-brilliant-friend/</link><pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/my-brilliant-friend/</guid><description/></item><item><title>One Hundreds Years of Solitude</title><link>https://notestoself.dev/reading/one-hundreds-years-of-solitude/</link><pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/one-hundreds-years-of-solitude/</guid><description/></item><item><title>The Almanack of Naval Ravikant</title><link>https://notestoself.dev/reading/the-almanack-of-naval-ravikant/</link><pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-almanack-of-naval-ravikant/</guid><description/></item><item><title>The Chinese Language: Fact and Fantasy</title><link>https://notestoself.dev/reading/the-chinese-language-fact-and-fantasy/</link><pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-chinese-language-fact-and-fantasy/</guid><description/></item><item><title>The Paper Menagerie</title><link>https://notestoself.dev/reading/the-paper-menagerie/</link><pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-paper-menagerie/</guid><description/></item><item><title>Upheaval</title><link>https://notestoself.dev/reading/upheaval/</link><pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/upheaval/</guid><description/></item><item><title>Utopia for Realists</title><link>https://notestoself.dev/reading/utopia-for-realists/</link><pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/utopia-for-realists/</guid><description/></item><item><title>丰乳肥臀</title><link>https://notestoself.dev/reading/%E4%B8%B0%E4%B9%B3%E8%82%A5%E8%87%80/</link><pubDate>Wed, 01 Jan 2020 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/%E4%B8%B0%E4%B9%B3%E8%82%A5%E8%87%80/</guid><description/></item><item><title>Setting up a Hugo static site inside Laravel on Forge</title><link>https://notestoself.dev/posts/setting-up-a-hugo-static-site-inside-laravel-on-forge/</link><pubDate>Mon, 23 Dec 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/setting-up-a-hugo-static-site-inside-laravel-on-forge/</guid><description>&lt;p&gt;Making a hybrid static + dynamic site can be quite a useful combination. This
might sound like an oxymoron, but it simply means serving static pages from a
site that also runs a dynamic web application. You can get some of the best of
both worlds with this approach.&lt;/p&gt;
&lt;p&gt;My &lt;a href="https://www.chineseboost.com/"&gt;Chinese-learning site ChineseBoost&lt;/a&gt; uses this
combination &amp;ndash; a lot of the content pages, e.g. for &lt;a href="https://www.chineseboost.com/grammar/"&gt;Chinese
grammar&lt;/a&gt;, are statically generated with
Hugo, while dynamic parts of the site are served by a Laravel application.
Similarly on our &lt;a href="https://www.poprobincards.co.uk/"&gt;pop out cards shop&lt;/a&gt;, we have
a static blog and an ecommerce application on the same server.&lt;/p&gt;</description></item><item><title>Song of Solomon</title><link>https://notestoself.dev/reading/song-of-solomon/</link><pubDate>Sun, 01 Dec 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/song-of-solomon/</guid><description/></item><item><title>Easy typed test mocks with Jest and TypeScript</title><link>https://notestoself.dev/posts/easy-typed-test-mocks-with-jest-and-typescript/</link><pubDate>Wed, 20 Nov 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/easy-typed-test-mocks-with-jest-and-typescript/</guid><description>&lt;p&gt;Here&amp;rsquo;s a quick and simple way to create type-safe test mocks with Jest in
TypeScript:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fooMock&lt;/span&gt;: &lt;span class="kt"&gt;jest.Mocked&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;IFoo&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="nx"&gt;fooMethod&lt;/span&gt;: &lt;span class="kt"&gt;jest.fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;testSubject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ClientClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fooMock&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This has a few useful properties:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The type system will enforce that the dependency is the correct type, i.e.
that &lt;code&gt;ClientClass&lt;/code&gt; is being passed the instance of &lt;code&gt;IFoo&lt;/code&gt; that it requires.&lt;/li&gt;
&lt;li&gt;The type system will enforce that the mock implements the required interface,
i.e. that it has stubs for all the required methods.&lt;/li&gt;
&lt;li&gt;The type system can still see that the methods on the mock are &lt;code&gt;jest.fn&lt;/code&gt;,
allowing things like &lt;code&gt;fooMock.fooMethod.mockReturnValue({})&lt;/code&gt; without
complaining.&lt;/li&gt;
&lt;li&gt;The IntelliJ IDE (e.g. WebStorm or IDEA) can automatically fill in the method
stubs for you once it sees the &lt;code&gt;jest.Mocked&amp;lt;IFoo&amp;gt;&lt;/code&gt; type annotation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can also use TypeScript&amp;rsquo;s handy &lt;code&gt;Pick&lt;/code&gt; utility type to get more specific
type-safe mocks:&lt;/p&gt;</description></item><item><title>Linking to the previous and next page in a taxonomy term in Hugo</title><link>https://notestoself.dev/posts/hugo-taxonomy-term-next-prev-page-links/</link><pubDate>Sat, 02 Nov 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/hugo-taxonomy-term-next-prev-page-links/</guid><description>&lt;p&gt;Something that can be slightly tricky to achieve within Hugo&amp;rsquo;s taxonomy system
is linking to the previous and next page within a particular taxonomy term. For
example, if you have a &lt;a href="https://notestoself.dev/posts/hugo-series-taxonomy/"&gt;Series taxonomy&lt;/a&gt;,
you might want to link to the previous and next page in that series on each page
in it.&lt;/p&gt;
&lt;p&gt;One way to solve this is to loop through all the pages in the taxonomy term and
grab the previous and next pages on the way.&lt;/p&gt;</description></item><item><title>Flexible test model factories in TypeScript</title><link>https://notestoself.dev/posts/flexible-test-model-factories-in-typescript/</link><pubDate>Wed, 23 Oct 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/flexible-test-model-factories-in-typescript/</guid><description>&lt;p&gt;Tests are improved by being more readable, and concise tests are more readable.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s good when a test has clear given, when, then steps, and the important
details are plain to see in the test code.&lt;/p&gt;
&lt;p&gt;This is often obstructed by large amounts of set-up and boilerplate code in the
test that distracts from the important details.&lt;/p&gt;
&lt;p&gt;Test model factories can reduce some of that boilerplate.&lt;/p&gt;
&lt;p&gt;For example, if we have a &lt;code&gt;Client&lt;/code&gt; model that contains quite a lot of detail,
setting one up in a test will be verbose. We might only be interested in testing
the effect of a single field in a single test, but setting up all the other
required fields obscures that intention.&lt;/p&gt;</description></item><item><title>Make a deep object delta diff in TypeScript</title><link>https://notestoself.dev/posts/make-a-deep-object-delta-diff-in-typescript/</link><pubDate>Wed, 25 Sep 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/make-a-deep-object-delta-diff-in-typescript/</guid><description>&lt;p&gt;Building on the &lt;a href="https://notestoself.dev/posts/enumerating-all-object-paths-in-typescript/"&gt;object path enumeration&lt;/a&gt;, we can also make a deep object diff
or delta.&lt;/p&gt;
&lt;p&gt;E.g. given these two objects:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;c&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;123&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;c&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;123&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;d&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;456&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;hello&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We would make this delta diff:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;a&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;b&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;d&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;456&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This uses a few utility functions aside from &lt;code&gt;enumeratePaths&lt;/code&gt;, but it should be
relatively clear what they do here:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;lodash&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;enumeratePaths&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;./enumeratePaths&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;objGet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;objSet&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="kr"&gt;from&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;./objPath&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;makeDelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;oldObject&lt;/span&gt;: &lt;span class="kt"&gt;IStringToUnknown&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;newObject&lt;/span&gt;: &lt;span class="kt"&gt;IStringToUnknown&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;IStringToUnknown&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;newObject&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;oldObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;newObject&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newObject&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;newObject&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paths&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uniq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;enumeratePaths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oldObject&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;enumeratePaths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newObject&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;pathSplits&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[][]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;pathSplits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;pathSplits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;thisPath&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;pathSplits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;otherPath&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;boolean&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;otherPath&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;thisPath&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;otherPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;thisPath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;delta&lt;/span&gt;: &lt;span class="kt"&gt;IStringToUnknown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;pathSplits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;pathSplit&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oldValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;objGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;oldObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pathSplit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;objGet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pathSplit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;oldValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;objSet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pathSplit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;delta&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We get the unique set of all possible paths in both objects (&lt;code&gt;O(n)&lt;/code&gt; for each
leaf key), and then compare each of those paths between the two objects so
overall this should be &lt;code&gt;O(2n)&lt;/code&gt; for each unique leaf key between the two objects.&lt;/p&gt;</description></item><item><title>Enumerating all object paths in TypeScript</title><link>https://notestoself.dev/posts/enumerating-all-object-paths-in-typescript/</link><pubDate>Mon, 23 Sep 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/enumerating-all-object-paths-in-typescript/</guid><description>&lt;p&gt;Here&amp;rsquo;s a small recursive TypeScript function to enumerate all the paths through
an object.&lt;/p&gt;
&lt;p&gt;E.g. given this object:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;animals&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;lion&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;sound&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;roar&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;zebra&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;appearance&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;stripes&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;elephant&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;size&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;large&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;features&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;trunk&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;yes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tusks&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;yes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;wings&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;no&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It enumerates these paths:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;animals&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;animals/lion&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;animals/zebra&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;animals/elephant&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;animals/lion/sound&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;animals/zebra/appearance&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;animals/elephant/size&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;animals/elephant/features&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;animals/elephant/features/trunk&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;animals/elephant/features/tusks&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;animals/elephant/features/wings&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The function:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;enumeratePaths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;: &lt;span class="kt"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;paths&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;object&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getOwnPropertyNames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getOwnPropertyNames&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;enumeratePaths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]).&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;subKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;subKey&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;paths&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Can you have multiple instances of your application in a single process?</title><link>https://notestoself.dev/posts/can-you-have-multiple-instances-of-your-application-in-a-single-process/</link><pubDate>Sun, 15 Sep 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/can-you-have-multiple-instances-of-your-application-in-a-single-process/</guid><description>&lt;p&gt;Here&amp;rsquo;s a question that might give some insight into the quality of your
codebase:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Can you have multiple instances of your application in a single process?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;By that I mean that you could easily create and run parts of the application
and/or the whole thing in one process, without this causing bad behaviour. If
so, this probably suggests that the design is quite well factored.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s a similar idea to the questions in the &lt;a href="https://www.joelonsoftware.com/2000/08/09/the-joel-test-12-steps-to-better-code/"&gt;Joel
test&lt;/a&gt;,
but for internal application structure.&lt;/p&gt;</description></item><item><title>Deepin Screenshot is a nice screenshot tool for Ubuntu Linux</title><link>https://notestoself.dev/posts/deepin-screenshot-ubuntu-linux/</link><pubDate>Sun, 18 Aug 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/deepin-screenshot-ubuntu-linux/</guid><description>&lt;p&gt;If you want a good screenshot tool on Ubuntu, &lt;a href="https://packages.ubuntu.com/focal/deepin-screenshot"&gt;Deepin
Screenshot&lt;/a&gt; is probably the
best one.&lt;/p&gt;
&lt;p&gt;It lets you drag-select a rectangular area of the screen, and then re-arrange
the area until you&amp;rsquo;re happy with it. You can then annotate the screenshot
selection with highlights, arrows, boxes and text. Finally, you can also choose
how to save the resulting screenshot image, or copy it to your clipboard.&lt;/p&gt;
&lt;p&gt;Deepin made it super easy to make the &lt;a href="https://www.poprobincards.co.uk/personal-gift-message-3d-pop-up-card/"&gt;personalised
cards&lt;/a&gt;
FAQ on our card shop, with a series of clear screenshots showing the process
with the key parts highlighted. This took a few minutes to make with Deepin.&lt;/p&gt;</description></item><item><title>Showing other pages with the same taxonomy term in Hugo</title><link>https://notestoself.dev/posts/hugo-showing-other-pages-taxonomy-term/</link><pubDate>Thu, 01 Aug 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/hugo-showing-other-pages-taxonomy-term/</guid><description>&lt;p&gt;Here&amp;rsquo;s a quick HTML template snippet to do something I find is quite a common
task in a Hugo site: show a list of other pages that share a taxonomy term with
the current page.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s probably a good idea to refresh your memory of &lt;a href="https://gohugo.io/content-management/taxonomies/"&gt;how taxonomies work in
Hugo&lt;/a&gt; if you&amp;rsquo;re feeling rusty.&lt;/p&gt;
&lt;p&gt;The snippet is just:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ if isset .Params &amp;#34;tags&amp;#34; }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ $tagTitle := index .Params.tags 0 }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ $currentLink := .Permalink }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;small&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Tag:&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;small&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="ni"&gt;&amp;amp;nbsp;&lt;/span&gt;{{ $tagTitle | title }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ range (index .Site.Taxonomies.tags ($tagTitle | urlize)).Pages.ByTitle }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ if eq $currentLink .Permalink }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ .Title }} &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;small&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;(this page)&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;small&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ else }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ .Permalink }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ .Title }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;li&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;ol&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The example taxonomy here is &amp;ldquo;tags&amp;rdquo;, but it could be any taxonomy. We check if
tags are set on this page, and then go ahead with showing other pages that share
a tag term with the current one if so.&lt;/p&gt;</description></item><item><title>Generating perspective-skewed mock-up images for printed products with Python</title><link>https://notestoself.dev/posts/python-generate-perspective-print-product-images/</link><pubDate>Sun, 21 Jul 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/python-generate-perspective-print-product-images/</guid><description>&lt;p&gt;For &lt;a href="https://momentwallart.co.uk/" title="Wall Art Poster Prints"&gt;Moment Wall Art&lt;/a&gt;
(and other printed product sites in future), we need to show the customer not
just the main print design itself but also mock-up images demonstrating how it
would look in various settings.&lt;/p&gt;
&lt;p&gt;Applying a perspective skew to images like this can be handled by the &lt;a href="https://docs.wand-py.org/en/0.6.5/"&gt;Python
Wand&lt;/a&gt; library, specifically with the
&lt;a href="https://docs.wand-py.org/en/0.6.5/guide/distortion.html#perspective"&gt;perspective
distortion&lt;/a&gt;
function.&lt;/p&gt;
&lt;p&gt;I have a Python script that iterates through a glob of all product files in
parallel, and generates these perspective-skewed mock up images where necessary.&lt;/p&gt;</description></item><item><title>Easier interface segregation in TypeScript (even better than Go!)</title><link>https://notestoself.dev/posts/easier-interface-segregation-in-typescript-even-better-than-go/</link><pubDate>Mon, 15 Jul 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/easier-interface-segregation-in-typescript-even-better-than-go/</guid><description>&lt;h2 id="preamble-introduction-to-interface-segregation"&gt;Preamble: Introduction to interface segregation&lt;/h2&gt;
&lt;p&gt;(Skip this if you&amp;rsquo;re aware of what the Interface Segration Principle is.)&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Interface_segregation_principle"&gt;Interface Segration
Principle&lt;/a&gt; is the
&lt;strong&gt;I&lt;/strong&gt; in the &lt;a href="https://en.wikipedia.org/wiki/SOLID"&gt;SOLID&lt;/a&gt; mnemonic. It&amp;rsquo;s
conceptually one of the simplest, but is often overlooked, which is a shame as
it&amp;rsquo;s a highly beneficial principle to follow in software design.&lt;/p&gt;
&lt;p&gt;The Interface Segration Principle is defined as &amp;ldquo;no client should be forced to
depend on methods it does not use&amp;rdquo;. More simply, it suggests that a client
should be able to depend on the smallest possible interface, i.e. exactly what
it needs and nothing more.&lt;/p&gt;</description></item><item><title>Start with "Given, When, Then" tests</title><link>https://notestoself.dev/posts/start-with-given-when-then-tests/</link><pubDate>Sat, 13 Jul 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/start-with-given-when-then-tests/</guid><description>&lt;p&gt;Whether or not you&amp;rsquo;re practicing test-driven development, it&amp;rsquo;s helpful to begin
writing tests with the &amp;ldquo;Given, When, Then&amp;rdquo; structure.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// Given we have a queued order;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// When we cancel it;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// Then the order should be cancelled.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Getting the structure in with comments first has several benefits.&lt;/p&gt;
&lt;p&gt;Firstly, it lowers the mental effort to required to start. You only need to
think about the desired behaviour in the simplest terms. What situation is this
relevant to; what happens; what should the result be? Once you&amp;rsquo;ve got that
written out it&amp;rsquo;s often easier to go on to the next steps.&lt;/p&gt;</description></item><item><title>The attacker really does know the system when someone leaves your company</title><link>https://notestoself.dev/posts/the-attacker-really-does-know-the-system-when-someone-leaves-your-company/</link><pubDate>Sat, 13 Jul 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/the-attacker-really-does-know-the-system-when-someone-leaves-your-company/</guid><description>&lt;p&gt;A common maxim of computer security is &amp;ldquo;the attacker knows the system&amp;rdquo;. In other
words, the system should still be secure even given a hypothetical attacker who
knows exactly how it works. It&amp;rsquo;s the flip-side of relying on &lt;a href="https://en.wikipedia.org/wiki/Security_through_obscurity"&gt;&amp;ldquo;security by
obscurity&amp;rdquo;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In cryptography, the same concept is referred to as &lt;a href="https://en.wikipedia.org/wiki/Kerckhoffs%27s_principle"&gt;Kerckhoffs&amp;rsquo;s
principle&lt;/a&gt;, but can be
applied to systems in general.&lt;/p&gt;
&lt;p&gt;Most engineers are aware of this concept, but it&amp;rsquo;s also common to find a vague
attitude that you can still &amp;ldquo;get away with&amp;rdquo; relying on little details that a
hypothetical attacker won&amp;rsquo;t actually know, e.g.&lt;/p&gt;</description></item><item><title>Fan-in from multiple routes to a single VM with iptables, for FIX on Quickfix</title><link>https://notestoself.dev/posts/fan-in-from-multiple-routes-to-a-single-vm-with-iptables-for-fix-on-quickfix/</link><pubDate>Mon, 08 Jul 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/fan-in-from-multiple-routes-to-a-single-vm-with-iptables-for-fix-on-quickfix/</guid><description>&lt;p&gt;Recently we were trying to set-up some network configuration to route
&lt;a href="https://en.wikipedia.org/wiki/Financial_Information_eXchange"&gt;FIX&lt;/a&gt; traffic from
a single Docker container on a VM instance through four of our VPN gateways, two
of the other side&amp;rsquo;s VPN gateways and on to two destination machines on the other
side. It&amp;rsquo;s somewhat complicated, but necessary due to some constraints being out
of our control.&lt;/p&gt;
&lt;p&gt;The ultimate goal is to have failover redundancy in the system so that a
different route can be used if one route is down.&lt;/p&gt;</description></item><item><title>Get to a failing test ASAP</title><link>https://notestoself.dev/posts/get-failing-test-asap/</link><pubDate>Mon, 08 Jul 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/get-failing-test-asap/</guid><description>&lt;p&gt;The &lt;a href="https://notestoself.dev/posts/specification-driven-development/"&gt;&amp;ldquo;Specification driven development&amp;rdquo;&lt;/a&gt; post mentions the idea of a &amp;ldquo;a
low-resistance workflow&amp;rdquo; that can be achieved with test-driven development.&lt;/p&gt;
&lt;p&gt;Overall that post is probably a bit dogmatic and over-eager about the idea of
test-driven development, but the point about achieving a low-resistance workflow
still seems valuable.&lt;/p&gt;
&lt;p&gt;A more trivial way of doing that is to use a simple workflow like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do you have a failing test? -&amp;gt; Make the test pass.&lt;/li&gt;
&lt;li&gt;No failing test? -&amp;gt; Make a failing test case.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A lot of the time, no further thought is required. This is a simpler
distillation of the idea of a low-resistance workflow. There is rarely a hard
decision to make, and you can keep moving forwards by doing either 1. or 2. in a
loop.&lt;/p&gt;</description></item><item><title>Pretty print JSON on the command line with Python</title><link>https://notestoself.dev/posts/pretty-print-json-on-the-command-line-with-python/</link><pubDate>Wed, 26 Jun 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/pretty-print-json-on-the-command-line-with-python/</guid><description>&lt;p&gt;I&amp;rsquo;m often dealing with little bits of JSON, and sometimes it&amp;rsquo;s helpful to
pretty-print them for readability.&lt;/p&gt;
&lt;p&gt;Python has a handy tool to make that easy on the command line: &lt;code&gt;json.tool&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;E.g.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;{&amp;#34;foo&amp;#34;: &amp;#34;bar&amp;#34;, &amp;#34;thing&amp;#34;: { &amp;#34;baz&amp;#34;: 42 } }&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; python -m json.tool
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;prints:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;foo&amp;#34;: &amp;#34;bar&amp;#34;,
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;thing&amp;#34;: {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;#34;baz&amp;#34;: 42
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If you&amp;rsquo;re on Mac, you can convert JSON in the clipboard to its pretty-printed
form with:&lt;/p&gt;</description></item><item><title>Be careful with truthy promises in TypeScript</title><link>https://notestoself.dev/posts/be-careful-with-truthy-promises-in-typescript/</link><pubDate>Sun, 23 Jun 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/be-careful-with-truthy-promises-in-typescript/</guid><description>&lt;p&gt;There&amp;rsquo;s a type of small &amp;ldquo;gotcha&amp;rdquo; bug that is easy to hit with promises for
boolean values in JavaScript.&lt;/p&gt;
&lt;p&gt;The simplest example might be this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;checkSomeCondition&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;checkSomeCondition&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="c1"&gt;// do something.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The if branch will always be executed, because the promise object returned by
&lt;code&gt;checkSomeCondition()&lt;/code&gt; will be evaluated as true, regardless of what it would
resolve to. It&amp;rsquo;s a typo-level mistake, but is easy to make.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s easier to miss this mistake when the condition has more parts, e.g.&lt;/p&gt;</description></item><item><title>Loop try catch</title><link>https://notestoself.dev/posts/loop-try-catch/</link><pubDate>Wed, 19 Jun 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/loop-try-catch/</guid><description>&lt;p&gt;A simple cause of failure in software systems is a single problematic item in a
set causing the entire set to fail.&lt;/p&gt;
&lt;p&gt;Trivially, it looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;processItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When something goes wrong in &lt;code&gt;processItem()&lt;/code&gt; and throws an error, either the
whole set fails to process, or all the subsequent items fail.&lt;/p&gt;
&lt;p&gt;The trivial example has a trivial improvement:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;processItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Do something useful with the error.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It might be desirable that the whole set fails if some items fail (like a
circuit breaker), but often it&amp;rsquo;s better that the other items go through.&lt;/p&gt;</description></item><item><title>(Cross-post) A Day in the Life of a Software Engineer at Freetrade</title><link>https://notestoself.dev/posts/day-life-software-engineer-freetrade/</link><pubDate>Sat, 01 Jun 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/day-life-software-engineer-freetrade/</guid><description>&lt;p&gt;(Cross-posted from
&lt;a href="https://blog.freetrade.io/a-day-in-the-life-of-a-software-engineer-at-freetrade-a39a590d7ffa"&gt;https://blog.freetrade.io/a-day-in-the-life-of-a-software-engineer-at-freetrade-a39a590d7ffa&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;I joined Freetrade as Senior Software Engineer in October 2018. We’ve got a huge
amount done in that time — the company’s moving fast!&lt;/p&gt;
&lt;p&gt;But behind the bustle of getting big features shipped, I wanted to give people
an insight into what an individual day’s work looks like on the engineering
team.&lt;/p&gt;
&lt;p&gt;Here’s my typical day as a Software Engineer at Freetrade.&lt;/p&gt;
&lt;h2 id="my-commute"&gt;(My) commute&lt;/h2&gt;
&lt;p&gt;I commute by bike 90% of the time. The Freetrade office in Shoreditch is about
10km away, so the showers at the office are essential. There’s also a space to
lock bikes safely off the street (no-one’s getting my bike)!&lt;/p&gt;</description></item><item><title>A bad mix of bash and sh with [[</title><link>https://notestoself.dev/posts/a-bad-mix-of-bash-and-sh-with/</link><pubDate>Thu, 23 May 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/a-bad-mix-of-bash-and-sh-with/</guid><description>&lt;p&gt;If you accidentally run a bash script with sh, you&amp;rsquo;re probably going to have a
bad time.&lt;/p&gt;
&lt;p&gt;One little gotcha is the use of &lt;code&gt;[[&lt;/code&gt; (double square brackets) in bash.&lt;/p&gt;
&lt;p&gt;E.g.:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; -z &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;PROJECT_ID is empty, exiting&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Bash will check the condition as expected. Sh, on the other hand, will reject
&lt;a href="https://serverfault.com/q/52034/294143"&gt;the double-brackets syntax&lt;/a&gt; that it is
not aware of with a message like this:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;[[: not found
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That wouldn&amp;rsquo;t be so bad, but by default it will then &lt;em&gt;continue running the
script&lt;/em&gt;.&lt;/p&gt;</description></item><item><title>Generate a UUID on the command line with Python</title><link>https://notestoself.dev/posts/generate-a-uuid-on-the-command-line-with-python/</link><pubDate>Sat, 18 May 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/generate-a-uuid-on-the-command-line-with-python/</guid><description>&lt;p&gt;Sometimes I want to manually generate a UUID for some test or example data.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s handy to have this Python snippet in Bash history to quickly do that:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python -c &lt;span class="s1"&gt;&amp;#39;import uuid; print(uuid.uuid4())&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Python National Insurance Number (NINo) one-line generator</title><link>https://notestoself.dev/posts/python-national-insurance-number-nino-one-line-generator/</link><pubDate>Tue, 23 Apr 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/python-national-insurance-number-nino-one-line-generator/</guid><description>&lt;p&gt;Here&amp;rsquo;s a Python one-liner for generating a random UK National Insurance Number
(aka NINo) on the command line, which is useful for testing apps that use one:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python -c &lt;span class="s1"&gt;&amp;#39;import random; import string; p=&amp;#34;ABCEGHJKLMNPRSTWXYZ&amp;#34;; print(&amp;#34;&amp;#34;.join([random.choice(p),random.choice(p)]+[random.choice(string.digits) for i in range(6)]+[random.choice(&amp;#34;ABCD&amp;#34;)]))&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To make it easier to use:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;nino&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;python -c &amp;#39;import random; import string; p=\&amp;#34;ABCEGHJKLMNPRSTWXYZ\&amp;#34;; print(\&amp;#34;\&amp;#34;.join([random.choice(p),random.choice(p)]+[random.choice(string.digits) for i in range(6)]+[random.choice(\&amp;#34;ABCD\&amp;#34;)]))&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Python random string one-liner for CLI</title><link>https://notestoself.dev/posts/python-random-string-one-liner-for-cli/</link><pubDate>Tue, 23 Apr 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/python-random-string-one-liner-for-cli/</guid><description>&lt;p&gt;Here&amp;rsquo;s a handy Python one-liner for generating a random string of letters and
digits on the command line:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;python -c &lt;span class="s1"&gt;&amp;#39;import random; import string; print(&amp;#34;&amp;#34;.join(random.choice(string.ascii_letters + string.digits) for i in range(30)))&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To make it easier to use:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;alias&lt;/span&gt; &lt;span class="nv"&gt;randomstring&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;python -c &amp;#39;import random; import string; print(\&amp;#34;\&amp;#34;.join(random.choice(string.ascii_letters + string.digits) for i in range(30)))&amp;#39;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Extract JSON config variables from the Airflow UI</title><link>https://notestoself.dev/posts/extract-json-config-variables-from-the-airflow-ui/</link><pubDate>Mon, 18 Mar 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/extract-json-config-variables-from-the-airflow-ui/</guid><description>&lt;p&gt;One way to configure &lt;a href="https://airflow.apache.org/"&gt;Apache Airflow&lt;/a&gt; is with
&amp;ldquo;Variables&amp;rdquo; that can be modified programmatically or via the Airflow UI.&lt;/p&gt;
&lt;p&gt;You can import the variables from a JSON file, again via the API or by uploading
the file in the UI. Sensibly, Airflow does not provide a way to export these
variables back out to a JSON file.&lt;/p&gt;
&lt;p&gt;However, if you find yourself in the situation where Airflow holds the correct
values and you&amp;rsquo;re not confident you&amp;rsquo;ve got them stored consistently elsewhere,
you might end up having to manually copy the variables back out of the Airflow
UI.&lt;/p&gt;</description></item><item><title>(Cross-post) Serverless integration testing at Freetrade</title><link>https://notestoself.dev/posts/serverless-integration-testing-freetrade/</link><pubDate>Mon, 11 Mar 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/serverless-integration-testing-freetrade/</guid><description>&lt;p&gt;(Cross-posted from &lt;a href="https://blog.freetrade.io/serverless-integration-testing-at-freetrade-5359fb0b0e57"&gt;https://blog.freetrade.io/serverless-integration-testing-at-freetrade-5359fb0b0e57&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;Our tech stack at Freetrade is 99% serverless. This means that for the vast
majority of our applications, the physical infrastructure, deployment and
other operational concerns are largely abstracted away by the cloud provider
(Google Cloud in our case).&lt;/p&gt;
&lt;p&gt;We focus on building what our users need, and our cloud provider focuses on
what we need to keep it running smoothly at scale.&lt;/p&gt;
&lt;p&gt;Serverless computing has huge benefits in terms of allowing more resources to
be devoted to solving user problems, but it also presents some different
challenges. One of those is how you go about applying automated testing to
your software systems when you’re in a serverless environment.&lt;/p&gt;</description></item><item><title>qrencode is a nice QR code CLI tool for Ubuntu Linux</title><link>https://notestoself.dev/posts/qrencode-qr-code-cli-tool-ubuntu-linux/</link><pubDate>Sat, 23 Feb 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/qrencode-qr-code-cli-tool-ubuntu-linux/</guid><description>&lt;p&gt;There&amp;rsquo;s something fun about QR codes, and I wanted an easy way generate them as
SVGs.&lt;/p&gt;
&lt;p&gt;Sure enough there&amp;rsquo;s a tool in the Ubuntu repos to do that:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo apt install qrencode
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s straightforward to use:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;qrencode -t SVG -o qr-code.svg &lt;span class="s1"&gt;&amp;#39;whatever you want encoded&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I wanted to generate a QR code to put on little business cards that we pack with
the orders for our &lt;a href="https://www.poprobincards.co.uk/"&gt;pop up card shop&lt;/a&gt;. The QR
code should point to a URL including a discount coupon that gets automatically
applied for customers who have arrived via that URL.&lt;/p&gt;</description></item><item><title>Promise batch progress meter in TypeScript</title><link>https://notestoself.dev/posts/promise-batch-progress-meter-in-typescript/</link><pubDate>Mon, 18 Feb 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/promise-batch-progress-meter-in-typescript/</guid><description>&lt;p&gt;Nodejs has a handy &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all"&gt;Promise.all&lt;/a&gt; function that awaits an entire batch of promises.
However, sometimes you want to see how that batch of promises is progressing
before the whole thing has finished.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a rough-and-ready way to print a progress meter to stdout as a batch of
promises completes in TypeScript:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;promises&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;: &lt;span class="kt"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;finished some work&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;finishedCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;promise&lt;/span&gt;: &lt;span class="kt"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;finishedCount&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;clearLine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cursorTo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;finishedCount&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; / &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 	&lt;span class="c1"&gt;// Handle errors.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That just prints and updates a line like &lt;code&gt;3 / 5&lt;/code&gt; to stdout. You could make it
fancier with a percentage or a visual progress meter.&lt;/p&gt;</description></item><item><title>Duolingo Fandom Japanese vocab CSV extractor in JS</title><link>https://notestoself.dev/posts/duolingo-fandom-japanese-js-csv-extractor/</link><pubDate>Sat, 16 Feb 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/duolingo-fandom-japanese-js-csv-extractor/</guid><description>&lt;p&gt;I&amp;rsquo;m casually going through the Japanese course in Duolingo, and found this
handy
&lt;a href="https://duolingo.fandom.com/wiki/Category:Japanese_skills"&gt;wiki series on Duolingo Fandom Japanese&lt;/a&gt;
with lists of all of the vocab.&lt;/p&gt;
&lt;p&gt;I wanted an easier way to import it into Anki, so I put together this little
pastable JS script to export a CSV of the vocab on one page:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sr"&gt;/^([\p{sc=Han}\p{sc=Hiragana}\p{sc=Katakana}？、，。]+)\s*\(([a-z\p{P}-]+)\)\s*=\s*([a-z\p{P}\(\) ?]+)(\s*\(([\p{sc=Han}\p{sc=Hiragana}\p{sc=Katakana}？、，。]+)\))?$/iu&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;csvRows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;日本語,ふりがな,ローマ字,英語&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;document&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;h3 ~ ul:not([class]) &amp;gt; li:not([class])&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;innerText&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerText&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;csvRow&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; /&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; /&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; /&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;,&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39; /&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#34;,&amp;#34;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;&amp;#34;&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;csvRow&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;csvRows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;csvRow&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;csv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;csvRows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;\n&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;download&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;a&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;download&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;href&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s1"&gt;&amp;#39;data:text/csv;charset=utf-8,&amp;#39;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;encodeURIComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;download&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;download&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;download&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;download&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;download&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can paste this into your browser devtools console on one of those pages, and
it will trigger a download of the vocab there as a CSV.&lt;/p&gt;</description></item><item><title>Creating a Series taxonomy in Hugo</title><link>https://notestoself.dev/posts/hugo-series-taxonomy/</link><pubDate>Wed, 30 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/hugo-series-taxonomy/</guid><description>&lt;p&gt;Hugo&amp;rsquo;s &lt;a href="https://gohugo.io/content-management/taxonomies/"&gt;taxonomies feature&lt;/a&gt; is
very flexible and covers a lot of use cases for organising static content.&lt;/p&gt;
&lt;p&gt;One thing I use it for on several static sites is to create a Series taxonomy
that allows an easy grouping of posts into a sequence. Readers can see other
posts in the series on each post, like a table of contents, they can see next
and previous links within the series, and they can view a dedicated list page
for the series as well.&lt;/p&gt;</description></item><item><title>Please comment your code</title><link>https://notestoself.dev/posts/please-comment-your-code/</link><pubDate>Wed, 23 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/please-comment-your-code/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Every comment represents a failure to make the code self explanatory.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&amp;amp;em; &lt;a href="https://blog.cleancoder.com/uncle-bob/2017/02/23/NecessaryComments.html"&gt;Robert C. Martin (Uncle Bob)&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;One of the better-known sentiments from Uncle Bob&amp;rsquo;s &lt;em&gt;Clean Code&lt;/em&gt; is that
comments are bad*. More precisely, the idea is that comments are a last resort
option to try and mitigate &amp;ldquo;bad code&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Sometimes this seems to lead to a logical fallacy that goes like this: &amp;ldquo;Comments
are a sign of bad code, and my code can&amp;rsquo;t be bad, so I won&amp;rsquo;t write comments&amp;rdquo;.
This can progress to an even worse line of thinking: &amp;ldquo;Comments are for bad code,
therefore if I don&amp;rsquo;t write comments, then my code can&amp;rsquo;t be bad&amp;rdquo;.&lt;/p&gt;</description></item><item><title>A Concise Guide to SSL/TLS for DevOps</title><link>https://notestoself.dev/reading/concise-guide-ssl-tls-devops/</link><pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/concise-guide-ssl-tls-devops/</guid><description/></item><item><title>Antifragile: Things that Gain from Disorder</title><link>https://notestoself.dev/reading/antifragile-things-gain-disorder/</link><pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/antifragile-things-gain-disorder/</guid><description/></item><item><title>Children of God</title><link>https://notestoself.dev/reading/children-of-god/</link><pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/children-of-god/</guid><description/></item><item><title>Don Quixote</title><link>https://notestoself.dev/reading/don-quixote/</link><pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/don-quixote/</guid><description/></item><item><title>Exploring CQRS and Event Sourcing</title><link>https://notestoself.dev/reading/exploring-cqrs-event-sourcing/</link><pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/exploring-cqrs-event-sourcing/</guid><description/></item><item><title>History of the Decline and Fall of the Roman Empire</title><link>https://notestoself.dev/reading/history-of-the-decline-and-fall-of-the-roman-empire/</link><pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/history-of-the-decline-and-fall-of-the-roman-empire/</guid><description/></item><item><title>Hyperion (Hyperion Cantos Book 1)</title><link>https://notestoself.dev/reading/hyperion-1/</link><pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/hyperion-1/</guid><description/></item><item><title>Jane Eyre</title><link>https://notestoself.dev/reading/jane-eyre/</link><pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/jane-eyre/</guid><description/></item><item><title>Origin Story: A Big History of Everything</title><link>https://notestoself.dev/reading/origin-story-big-history-everything/</link><pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/origin-story-big-history-everything/</guid><description/></item><item><title>Systemantics: The Systems Bible</title><link>https://notestoself.dev/reading/systems-bible/</link><pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/systems-bible/</guid><description/></item><item><title>The Confidence Game</title><link>https://notestoself.dev/reading/the-confidence-game/</link><pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-confidence-game/</guid><description/></item><item><title>The Most Human Human</title><link>https://notestoself.dev/reading/the-most-human-human/</link><pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-most-human-human/</guid><description/></item><item><title>The New Silk Roads: The Present and Future of the World</title><link>https://notestoself.dev/reading/new-silk-roads-present-future-world/</link><pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/new-silk-roads-present-future-world/</guid><description/></item><item><title>The Silk Roads: A New History of the World</title><link>https://notestoself.dev/reading/silk-roads-new-history-world/</link><pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/silk-roads-new-history-world/</guid><description/></item><item><title>The Three Body Problem</title><link>https://notestoself.dev/reading/the-three-body-problem/</link><pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-three-body-problem/</guid><description/></item><item><title>Why We Sleep</title><link>https://notestoself.dev/reading/why-we-sleep/</link><pubDate>Tue, 01 Jan 2019 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/why-we-sleep/</guid><description/></item><item><title>Finding the max branch sum of a sequentially represented binary tree in Python</title><link>https://notestoself.dev/posts/finding-the-max-branch-sum-of-a-sequentially-represented-binary-tree-in-python/</link><pubDate>Sun, 23 Dec 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/finding-the-max-branch-sum-of-a-sequentially-represented-binary-tree-in-python/</guid><description>&lt;p&gt;Given a binary tree represented sequentially as a list of integer node values,
we want to identify whether the sum of the left branch or the sum of th right
branch is larger. In other words we want to identify which branch has the
maximum sum.&lt;/p&gt;
&lt;p&gt;For example, this binary tree:&lt;/p&gt;
&lt;pre class="text-diagram"&gt;
····································
················┌──┐················
00··············│05│················
················└──┘················
··········╱··············╲··········
········┌──┐············┌──┐········
01······│04│············│06│········
········└──┘············└──┘········
······╱······╲······················
····┌──┐····┌──┐····················
02··│16│····│12│····················
····└──┘····└──┘····················
····································
&lt;/pre&gt;


&lt;p&gt;Could be represented sequentially as &lt;code&gt;[5, 4, 6, 16, 12]&lt;/code&gt;. Missing nodes are
represented as &lt;code&gt;-1&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>(Cross-post) Why we use the Firebase Realtime Database at Freetrade</title><link>https://notestoself.dev/posts/cross-post-coming-to-firebases-nosql-realtime-database-from-a-sql-background/</link><pubDate>Fri, 21 Dec 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/cross-post-coming-to-firebases-nosql-realtime-database-from-a-sql-background/</guid><description>&lt;p&gt;(Cross-posted from
&lt;a href="https://blog.freetrade.io/coming-to-firebases-nosql-realtime-database-from-a-sql-background-b605b2910c4c"&gt;https://blog.freetrade.io/coming-to-firebases-nosql-realtime-database-from-a-sql-background-b605b2910c4c&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;We use the Firebase Realtime Database at Freetrade, as part of our
infrastructure on Google Cloud.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The Firebase Realtime Database is a cloud-hosted NoSQL database that lets you
store and sync data between your users in realtime.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Firebase Realtime Database has a markedly different development philosophy to
the more traditional SQL databases that many developers are used to. Here’s a
rundown of what it’s like coming to Firebase Realtime Database from an SQL
background. How the Firebase Realtime Database is different&lt;/p&gt;</description></item><item><title>Spoonerism generator Python script</title><link>https://notestoself.dev/posts/spoonerism-generator-python-script/</link><pubDate>Thu, 08 Nov 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/spoonerism-generator-python-script/</guid><description>&lt;p&gt;&amp;ldquo;What&amp;rsquo;s the Difference Between&amp;rdquo; is a childish word-play game where you give
clues for other players to guess a
&lt;a href="https://en.wikipedia.org/wiki/Spoonerism"&gt;spoonerism&lt;/a&gt; phrase pair. For example:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Q: &amp;ldquo;What&amp;rsquo;s the difference between stress on a group, and an old locomotive?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;A: One is team strain, the other&amp;rsquo;s a steam train.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;m part of a WhatsApp group that sends these back and forth on a daily basis
(usually with less innocent contents than that example).&lt;/p&gt;</description></item><item><title>Keeping the ElasticSearch service running on an Ubuntu server</title><link>https://notestoself.dev/posts/keep-elasticsearch-service-running-ubuntu-server/</link><pubDate>Sun, 28 Oct 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/keep-elasticsearch-service-running-ubuntu-server/</guid><description>&lt;p&gt;A local ElasticSearch instance powers the &lt;a href="https://www.chineseboost.com/chinese-example-sentences"&gt;Chinese example sentence
search&lt;/a&gt; feature on
&lt;em&gt;Chinese Boost&lt;/em&gt;. This works fine, but the ElasticSearch instance has a habit of
crashing about once a day.&lt;/p&gt;
&lt;p&gt;While I&amp;rsquo;ve been investigating why it crashes and how to prevent that from
happening, I also wanted to have the EC2 Ubuntu server it runs on restart the
ElasticSearch service automatically as a workaround.&lt;/p&gt;
&lt;p&gt;This can be done using systemd.&lt;/p&gt;
&lt;p&gt;You can add extra config for the &lt;code&gt;elasticsearch&lt;/code&gt; service with this command:&lt;/p&gt;</description></item><item><title>Be careful with the S3 origin URL for a Cloudfront distribution</title><link>https://notestoself.dev/posts/careful-cloudfront-s3-origin-url/</link><pubDate>Tue, 23 Oct 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/careful-cloudfront-s3-origin-url/</guid><description>&lt;p&gt;Be careful with the S3 origin URL for a Cloudfront distribution.&lt;/p&gt;
&lt;p&gt;When using an S3 bucket with static website hosting enabled as the origin for a
Cloudfront distribution, you must use the static website endpoint, and not the
S3 bucket URL. These are different.&lt;/p&gt;
&lt;p&gt;The S3 bucket URL looks like this (&lt;strong&gt;don&amp;rsquo;t use this for the Cloudfront origin&lt;/strong&gt;):&lt;/p&gt;
&lt;p&gt;&lt;a href="https://s3.eu-west-2.amazonaws.com/foobar-bucket-name/"&gt;https://s3.eu-west-2.amazonaws.com/foobar-bucket-name/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The S3 static website hosting URL looks like this (&lt;strong&gt;use this for the Cloudfront origin&lt;/strong&gt;):&lt;/p&gt;</description></item><item><title>Making a helpful catalogue data reporting command in Laravel</title><link>https://notestoself.dev/posts/laravel-catalogue-data-report-command/</link><pubDate>Tue, 23 Oct 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/laravel-catalogue-data-report-command/</guid><description>&lt;p&gt;The catalogue data model in our &lt;a href="https://www.poprobincards.co.uk/"&gt;3D pop up
cards&lt;/a&gt; shop includes Product and Tag entities.
Tags are a flat structure with a many-to-many relationship to Products, to allow
some simple tagging to help customers find cards with particular elements.&lt;/p&gt;
&lt;p&gt;The Tag-Product relationship is managed manually in the backoffice UI, which is
fine but can be error prone when we miss Products that should have a particular
Tag.&lt;/p&gt;
&lt;p&gt;To make this easier to work with, I put together a quick command line tool in
Laravel that identifies Products that seem to be missing an appriate Tag.&lt;/p&gt;</description></item><item><title>Freetrade</title><link>https://notestoself.dev/work/freetrade/</link><pubDate>Mon, 08 Oct 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/freetrade/</guid><description>&lt;p&gt;I was Principal Software Engineer at Freetrade, primarily working on the
platform but also some client-side work. Freetrade has a serverless architecture
running Google Cloud Functions in Firebase using TypeScript.&lt;/p&gt;</description></item><item><title>A quick Python script to help me translate classical Chinese poetry</title><link>https://notestoself.dev/posts/chinese-poetry-python-script/</link><pubDate>Sat, 06 Oct 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/chinese-poetry-python-script/</guid><description>&lt;p&gt;One of my pastimes is translating Chinese poetry and other text into English on
my &lt;a href="https://eastasiastudent.net/"&gt;East Asia Student&lt;/a&gt; blog. The translations I
make are more like annotations, as I try to adhere as closely as I can to the
structure and literal meaning of the original text. The idea is that the
translation is helpful for reading the original Chinese text, rather than being
a full rendering into English.&lt;/p&gt;
&lt;p&gt;One aspect of this is the inclusion of pinyin and literal glosses for each line
of the poem. For example:&lt;/p&gt;</description></item><item><title>Island counting algorithm in Typescript</title><link>https://notestoself.dev/posts/island-counting-algorithm-typescript/</link><pubDate>Sun, 23 Sep 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/island-counting-algorithm-typescript/</guid><description>&lt;p&gt;A friend mentioned a code screen interview question he likes to ask based on
counting the number of discrete &amp;ldquo;islands&amp;rdquo; in a grid:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Given a square grid of cells, some of which are sea (0) and some of which
are land (1), implement an island counter that identifies each contiguous
area of land cells (an island) and counts the total number of such islands.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This seems like quite a fun problem to solve so I had a go at it in TypeScript.&lt;/p&gt;</description></item><item><title>AlgoDaily 22: Count the Planes</title><link>https://notestoself.dev/posts/algodaily-22-count-the-planes/</link><pubDate>Wed, 05 Sep 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-22-count-the-planes/</guid><description>&lt;p&gt;&lt;a href="https://algodaily.com/challenges/count-the-planes"&gt;https://algodaily.com/challenges/count-the-planes&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is the same as the &lt;a href="https://notestoself.dev/posts/island-counting-algorithm-typescript/"&gt;island counting problem&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;enumerateGrid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;enumerateNeighbours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;:-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;},{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;:-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;},{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;V&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;enumerateNeighbours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;P&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;numOfPlanes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;planesCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;enumerateGrid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;P&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="nx"&gt;planesCount&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="nx"&gt;visit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;y&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;planesCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You iterate over the grid cells. Each time you hit a &amp;ldquo;plane&amp;rdquo; cell, increment the
counter, then recursively mark it and all its neighbouring &amp;ldquo;plane&amp;rdquo; cells as
visited.&lt;/p&gt;</description></item><item><title>Unix Fizz-Buzz one-liner</title><link>https://notestoself.dev/posts/unix-fizz-buzz-one-liner/</link><pubDate>Mon, 27 Aug 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/unix-fizz-buzz-one-liner/</guid><description>&lt;p&gt;Given that &amp;ldquo;implement Fizz-Buzz in a language of your choice&amp;rdquo; is quite a common
programming interview task, I was wondering if it would be possible to do it in
one line on the command line using Unix tools.&lt;/p&gt;
&lt;p&gt;Unsurprisingly it is; here&amp;rsquo;s my version:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;seq &lt;span class="m"&gt;100&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; awk &lt;span class="s1"&gt;&amp;#39;{ if ($0 % 15 == 0) { print &amp;#34;fizzbuzz&amp;#34; } else if ($0 % 3 == 0) { print &amp;#34;fizz&amp;#34; } else if ($0 % 5 == 0) { print &amp;#34;buzz&amp;#34; } else { print $0 } }&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;seq&lt;/code&gt; at the beginning generates numbers from 1 to 100, and then the awk
command does a basic if-else on each line to decide what to print.&lt;/p&gt;</description></item><item><title>AlgoDaily 21: Contiguous Subarray Sum</title><link>https://notestoself.dev/posts/algodaily-21-contiguous-subarray-sum/</link><pubDate>Thu, 23 Aug 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-21-contiguous-subarray-sum/</guid><description>&lt;p&gt;&lt;a href="https://algodaily.com/challenges/contiguous-subarray-sum"&gt;https://algodaily.com/challenges/contiguous-subarray-sum&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s a fairly obvious brute-force &lt;code&gt;O(n^2)&lt;/code&gt; way to do this by looping through
every possible sub-array:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;allSubArrays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;slice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;j&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;				&lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;subarraySum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targetSum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sub&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;allSubArrays&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sub&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;targetSum&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is &lt;code&gt;O(n^2)&lt;/code&gt;, so not great, but I can&amp;rsquo;t think of a different way to do it.
We could make some small optimisations by skipping a subarray as soon as
possible, but the worst case would still be &lt;code&gt;O(n^2)&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>AlgoDaily 20: Uniqueness of Arrays</title><link>https://notestoself.dev/posts/algodaily-20-uniqueness-of-arrays/</link><pubDate>Wed, 08 Aug 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-20-uniqueness-of-arrays/</guid><description>&lt;p&gt;&lt;a href="https://algodaily.com/challenges/uniqueness-of-arrays"&gt;https://algodaily.com/challenges/uniqueness-of-arrays&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This is pretty trivial with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set"&gt;Set&lt;/a&gt; class in ECMAScript 2015.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;uniques&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If that seems like cheating, then we can achieve this with a hash map and
a new array:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;uniques&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;unique&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="nx"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;unique&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is &lt;code&gt;O(n)&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>AlgoDaily 19: Fast Minimum In Rotated Array</title><link>https://notestoself.dev/posts/algodaily-19-fast-minimum-in-rotated-array/</link><pubDate>Sun, 05 Aug 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-19-fast-minimum-in-rotated-array/</guid><description>&lt;p&gt;&lt;a href="https://algodaily.com/challenges/fast-minimum-in-rotated-array"&gt;https://algodaily.com/challenges/fast-minimum-in-rotated-array&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It seems we have to identify an element that is lower than the preceding one, as
this must be the original beginning of the array and thus the lowest. If we get
to the end of the array without finding a drop, then it must be the first
element of the array, as the pivot was at the end.&lt;/p&gt;
&lt;p&gt;This still has a worst case running time of &lt;code&gt;O(n)&lt;/code&gt;, though, as we might have to
iterate through the whole array.&lt;/p&gt;</description></item><item><title>AlgoDaily 18: Sum All Primes</title><link>https://notestoself.dev/posts/algodaily-18-sum-all-primes/</link><pubDate>Mon, 23 Jul 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-18-sum-all-primes/</guid><description>&lt;p&gt;&lt;a href="https://algodaily.com/challenges/sum-all-primes"&gt;https://algodaily.com/challenges/sum-all-primes&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I remember there&amp;rsquo;s an algorithm called the &lt;a href="https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes"&gt;Sieve of
Eratosthenes&lt;/a&gt; for finding
prime numbers up to a limit, which seems like it would be useful here.&lt;/p&gt;
&lt;p&gt;To apply the Sieve of Eratosthenes, you first allocate an object with each
integer up to &lt;code&gt;n&lt;/code&gt; as the keys.&lt;/p&gt;
&lt;p&gt;You then iterate on integers up to $\sqrt{n}$. In other words, keep iterating on
integers &lt;code&gt;p&lt;/code&gt; until &lt;code&gt;p * p&lt;/code&gt; would be more than &lt;code&gt;n&lt;/code&gt;. E.g. if &lt;code&gt;n = 25&lt;/code&gt;, then the
iteration should stop at 5, because &lt;code&gt;5 * 5 = 25&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Fixing cut off stroke edges of shapes in Inkscape</title><link>https://notestoself.dev/posts/inkscape-shape-stroke-edges-cut-off/</link><pubDate>Mon, 23 Jul 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/inkscape-shape-stroke-edges-cut-off/</guid><description>&lt;p&gt;I got stuck with this problem in Inkscape where the stroke edges of shapes seem
to get cut off by the bounding box:&lt;/p&gt;
&lt;figure&gt;
&lt;img
	src='https://notestoself.dev/posts/inkscape-shape-stroke-edges-cut-off/inkscape-shape-stroke-edges-cut-off.png'
	alt='Stroke edges of shapes cut off in Inkscape'
	title='Stroke edges of shapes cut off in Inkscape'
&gt;
&lt;/figure&gt;

&lt;p&gt;After a bit of searching, it turned out that this was caused by having a small
blur set on the layer containing the shapes:&lt;/p&gt;
&lt;figure&gt;
&lt;img
	src='https://notestoself.dev/posts/inkscape-shape-stroke-edges-cut-off/inkscape-z-layer-blur.png'
	alt='Stroke edges of shapes cut off in Inkscape'
	title='Stroke edges of shapes cut off in Inkscape'
&gt;
&lt;/figure&gt;

&lt;p&gt;Setting that layer blur back to zero restores the full stroke edges of the
shapes.&lt;/p&gt;</description></item><item><title>AlgoDaily 17: Find Missing Number in Array</title><link>https://notestoself.dev/posts/algodaily-17-find-missing-number-in-array/</link><pubDate>Sun, 08 Jul 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-17-find-missing-number-in-array/</guid><description>&lt;p&gt;&lt;a href="https://algodaily.com/challenges/find-missing-number-in-array"&gt;https://algodaily.com/challenges/find-missing-number-in-array&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Seems like we should iterate on the array, adding an integer to the result array
if it&amp;rsquo;s not present in the input. We have to backtrack the iteration by one each
time we hit a missing number. This is &lt;code&gt;O(n)&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;missingNumbers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sequence&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;missing&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;sequence&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="nx"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sequence&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="nx"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;expected&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="nx"&gt;expected&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;missing&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The solution suggested by &lt;em&gt;Algo Daily&lt;/em&gt; is similar, but uses an inner loop to
insert all of the missing numbers in a gap at once, rather than doing them one
iteration at a time as in the solution above. This avoids the need to backtrack.&lt;/p&gt;</description></item><item><title>AlgoDaily 16: Missing Number In Unsorted</title><link>https://notestoself.dev/posts/algodaily-16-missing-number-in-unsorted/</link><pubDate>Thu, 05 Jul 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-16-missing-number-in-unsorted/</guid><description>&lt;p&gt;&lt;a href="https://algodaily.com/challenges/missing-number-in-unsorted"&gt;https://algodaily.com/challenges/missing-number-in-unsorted&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The requirement to do this without sorting makes me think of using a hash map to
track each number we do see, and then finding the missing integer there by
iterating across the expected range. This isn&amp;rsquo;t &lt;code&gt;O(n)&lt;/code&gt;, though; it&amp;rsquo;s &lt;code&gt;O(m+n)&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;missingInUnsorted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lowerBound&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;upperBound&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;carry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="nx"&gt;carry&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;carry&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lowerBound&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;upperBound&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;em&gt;Algo Daily&lt;/em&gt; has a solution using the Gauss sum formula &lt;code&gt;n(n+1)/2&lt;/code&gt; to determine
the sum of a range of numbers, and then subtracting the sum of the given numbers
from that to find the single missing integer.&lt;/p&gt;</description></item><item><title>AlgoDaily 15: Max of Min Pairs</title><link>https://notestoself.dev/posts/algodaily-15-max-of-min-pairs/</link><pubDate>Sat, 23 Jun 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-15-max-of-min-pairs/</guid><description>&lt;p&gt;&lt;a href="https://algodaily.com/challenges/max-of-min-pairs"&gt;https://algodaily.com/challenges/max-of-min-pairs&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This requires a bit of thought. We want to get the highest possible smaller
number in each pair.&lt;/p&gt;
&lt;p&gt;The first thought is that we need to sort the numbers before we do anything
else, so that we can allocate pairs with the largest possible minimum.&lt;/p&gt;
&lt;p&gt;To get the largest possible minimum in the pair, you have to &amp;ldquo;waste&amp;rdquo; a larger
number to accompany it. This should be the smallest possible number that is
larger than the minimum in the pair.&lt;/p&gt;</description></item><item><title>Simple estimated delivery dates in vanilla JS</title><link>https://notestoself.dev/posts/simple-estimated-delivery-dates-in-vanilla-js/</link><pubDate>Wed, 20 Jun 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/simple-estimated-delivery-dates-in-vanilla-js/</guid><description>&lt;p&gt;Displaying estimated delivery dates is a handy little feature in e-commerce that
gives useful information to customers and reassures them about the ordering
process.&lt;/p&gt;
&lt;p&gt;Estimating delivery dates can be quite involved, but it&amp;rsquo;s easy to chuck a very
simple estimated delivery range on any page with these vanilla JavaScript
functions, no libraries required:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;showEstimatedDelivery&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;estDelEls&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;.estimated-delivery&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;estDelEls&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;estDelEls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;minDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxDate&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;estimatedDeliveryRange&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;minFormat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxFormat&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;formatDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minDate&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;formatDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxDate&lt;/span&gt;&lt;span class="p"&gt;)];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;estimatedDelivery&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minFormat&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;maxFormat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;estimatedDelivery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;minFormat&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;estimatedDelivery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;minFormat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; &amp;amp;mdash; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;maxFormat&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;.estimated-delivery&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;estimatedDelivery&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SATURDAY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SUNDAY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;estimatedDeliveryRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;from&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;minWorkDays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxWorkDays&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;from&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;minDate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;maxDate&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minWorkDays&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;maxWorkDays&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getHours&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getDay&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;SATURDAY&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getDay&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;SUNDAY&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;minWorkDays&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;maxWorkDays&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minWorkDays&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;minDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;minDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxWorkDays&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;maxDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;maxDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getTime&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;minDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;maxDate&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;formatDate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Intl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DateTimeFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;en-GB&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;weekday&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;short&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;day&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;numeric&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;month&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;short&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;date&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We use this on our static e-commerce site &lt;a href="https://momentwallart.co.uk/"&gt;Moment Wall
Art&lt;/a&gt;, for example on this &lt;a href="https://momentwallart.co.uk/keinen-kacho-gafu-pl-06-1891-1892-japanese-wall-art-portrait/"&gt;Japanese wall art
print&lt;/a&gt;
product page.&lt;/p&gt;</description></item><item><title>Python asyncio concurrent map pool function</title><link>https://notestoself.dev/posts/python-asyncio-concurrent-map-pool/</link><pubDate>Mon, 18 Jun 2018 13:18:51 +0100</pubDate><guid>https://notestoself.dev/posts/python-asyncio-concurrent-map-pool/</guid><description>&lt;p&gt;Here is a relatively short Python function to map input items concurrently, with
a fixed pool size to limit the maximum concurrency. The work function to map
each item is a normal synchronous function, which makes this easy to use in
projects that are not otherwise using asyncio.&lt;/p&gt;
&lt;p&gt;This function should be OK to run with or without a pre-existing event loop in
the process. It uses an existing event loop if there is one, otherwise it
creates a new event loop and uses that. Either way, the calling code does not
need to know that this function is using asyncio internally.&lt;/p&gt;</description></item><item><title>Clipboard hygiene</title><link>https://notestoself.dev/posts/clipboard-hygiene/</link><pubDate>Tue, 12 Jun 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/clipboard-hygiene/</guid><description>&lt;p&gt;After using the excellent &lt;a href="https://www.keepassx.org/"&gt;keepassx password manager&lt;/a&gt;
for a while, I&amp;rsquo;ve noticed it has a nice little security feature: once you copy
a password to the clipboard, keepassx waits for a short period of time and then
clears your clipboard. It also clears the clipboard when you close keepassx.&lt;/p&gt;
&lt;p&gt;This is a good idea as it somewhat reduces the chance of some other process
being able to steal a password from the clipboard (which any user-level process
could do). More usefully, it also reduces the chance of you accidentally pasting
the password somewhere you didn&amp;rsquo;t want to.&lt;/p&gt;</description></item><item><title>AlgoDaily 14: Find First Non-Repeating Character</title><link>https://notestoself.dev/posts/algodaily-14-find-first-non-repeating-character/</link><pubDate>Fri, 08 Jun 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-14-find-first-non-repeating-character/</guid><description>&lt;p&gt;&lt;a href="https://algodaily.com/challenges/find-first-non-repeating-character"&gt;https://algodaily.com/challenges/find-first-non-repeating-character&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;This shouldn&amp;rsquo;t be too hard with a hash map of characters to counts, relying on
JavaScript objects having ordered keys to then find the first character with
a single occurrence.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;firstNonRepeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="kr"&gt;char&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="nx"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;char&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;char&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;char&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="kr"&gt;char&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;char&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kr"&gt;char&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>AlgoDaily 13: Dollar Sign Deletion</title><link>https://notestoself.dev/posts/algodaily-13-dollar-sign-deletion/</link><pubDate>Tue, 05 Jun 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-13-dollar-sign-deletion/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;You&amp;rsquo;re given an array of strings containing alphabetical characters and
certain $ characters. A $ represents a DELETE action whereby the character
before it is deleted.&lt;/p&gt;
&lt;p&gt;Each of these strings will be run through a method to operate on the $ DELETE
action. We want to find out if the final string is the same for all of them.
Let&amp;rsquo;s take an example:&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;f$st&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;st&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;isDollarDeleteEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// true because both become &amp;#34;st&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Given the below function signature, can you find a solution in O(n) time and
constant space?&amp;rdquo;&lt;/p&gt;</description></item><item><title>Extracting product image main colours in Python</title><link>https://notestoself.dev/posts/extracting-product-image-main-colours-python/</link><pubDate>Sat, 19 May 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/extracting-product-image-main-colours-python/</guid><description>&lt;p&gt;The main colours of a product are often quite important data in e-commerce
systems, and it&amp;rsquo;s nice to have an automated way to extract these.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a rough summary of what I ended doing in Python to get this data from
product images.&lt;/p&gt;
&lt;p&gt;It uses three different colour extraction libraries and merges the results from
them, which is somewhat questionable but seems to provide better overall results
than a single library alone.&lt;/p&gt;</description></item><item><title>Year name to century name and decade name in Python</title><link>https://notestoself.dev/posts/year-name-century-name-decade-name-python/</link><pubDate>Sat, 19 May 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/year-name-century-name-decade-name-python/</guid><description>&lt;p&gt;Here are some little helper functions to convert an integer year into a century
name and decade name, for example &lt;code&gt;1989&lt;/code&gt; would be &lt;code&gt;&amp;quot;20th Century&amp;quot;&lt;/code&gt; and
&lt;code&gt;&amp;quot;1980s&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;math&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;floor&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;year_to_century_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;century&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;suffix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ordinal_suffix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;century&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;century&lt;/span&gt;&lt;span class="si"&gt;}{&lt;/span&gt;&lt;span class="n"&gt;suffix&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; Century&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;ordinal_suffix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;num&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;st&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;nd&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;rd&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;num&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;th&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;year_to_decade_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;decade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;year&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;decade&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;s&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We use this in &lt;a href="https://momentwallart.co.uk/"&gt;Moment Wall Art&lt;/a&gt; to extract the
century and decade of each piece, which allows for easy taxonomy pages like
&lt;a href="https://momentwallart.co.uk/20th-century-wall-art-poster-prints/"&gt;20th Century Wall
Art&lt;/a&gt; or &lt;a href="https://momentwallart.co.uk/2020s-wall-art-poster-prints/"&gt;2020s
Wall Art&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Allow a Django command to use a file or stdin / stdout</title><link>https://notestoself.dev/posts/django-command-file-stdin-stdout/</link><pubDate>Sun, 13 May 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/django-command-file-stdin-stdout/</guid><description>&lt;p&gt;Here&amp;rsquo;s a little approach I&amp;rsquo;ve started using when writing Python scripts and
Django console commands. It allows them to easily handle i/o from the
commandline or from a file without much effort.&lt;/p&gt;
&lt;p&gt;Rather than using &lt;code&gt;sys.stdin&lt;/code&gt; or &lt;code&gt;sys.stdout&lt;/code&gt; directly, you define an
&lt;code&gt;argparse.FileType&lt;/code&gt; argument for the command, and set the default to
&lt;code&gt;sys.stdin&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For example, in a Django command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stdout&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;argparse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FileType&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core.management.base&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BaseCommand&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Command&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseCommand&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_arguments&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ArgumentParser&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;input&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FileType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;r&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;parser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add_argument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;output&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;?&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FileType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;w&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;input&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;output&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Now the command is flexible as you can pipe data to or from it:&lt;/p&gt;</description></item><item><title>Streaming a CSV file from the database in Laravel</title><link>https://notestoself.dev/posts/laravel-stream-csv-database/</link><pubDate>Sun, 13 May 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/laravel-stream-csv-database/</guid><description>&lt;p&gt;Exporting data to CSV is a requirement in almost any business software project.
We needed it to get a convenient sales data export in our &lt;a href="https://www.poprobincards.co.uk/" title="3D Pop Up Cards"&gt;card
shop&lt;/a&gt; sideproject recently,
and it turned out to be straightforward but slightly weird to do in Laravel.&lt;/p&gt;
&lt;h2 id="sales-data-export-query"&gt;Sales data export query&lt;/h2&gt;
&lt;p&gt;The &lt;a href="https://github.com/hughgrigg/poprobincards/blob/master/resources/queries/stock-sales-report.sql"&gt;sales data export
query&lt;/a&gt;
is about 30 lines of SQL. This was easier to write directly in SQL than wrapping
it up in Laravel&amp;rsquo;s ORM, and it&amp;rsquo;s also handy to be able to run this query
separately anyway.&lt;/p&gt;</description></item><item><title>AlgoDaily 12: Detect Substring in String</title><link>https://notestoself.dev/posts/algodaily-12-detect-substring-in-string/</link><pubDate>Tue, 08 May 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-12-detect-substring-in-string/</guid><description>&lt;p&gt;&lt;a href="https://algodaily.com/challenges/detect-substring-in-string"&gt;https://algodaily.com/challenges/detect-substring-in-string&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;How would you write a function to detect a substring in a string?&lt;/p&gt;
&lt;p&gt;If the substring can be found in the string, return the index at which it
starts. Otherwise, return -1.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We can iterate through the string, and increment a counter when we hit that
index in the target substring. If the counter gets to the length of the target
substring, we can return the iterator index minus the counter.&lt;/p&gt;</description></item><item><title>AlgoDaily 11: Sum Digits Until One</title><link>https://notestoself.dev/posts/algodaily-11-sum-digits-until-one/</link><pubDate>Sun, 06 May 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-11-sum-digits-until-one/</guid><description>&lt;p&gt;&lt;a href="https://algodaily.com/challenges/sum-digits-until-one"&gt;https://algodaily.com/challenges/sum-digits-until-one&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;We&amp;rsquo;re provided a positive integer num. Can you write a method to repeatedly
add all of its digits until the result has only one digit?&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an example: if the input was 49, we&amp;rsquo;d go through the following steps:&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// start with 49
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;4 + 9 = 13
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;// move onto 13
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1 + 3 = 4
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This seems to mostly be an issue of type juggling. If we&amp;rsquo;re careful with types,
then all we have to do is keep breaking down the string of the number into
single digit characters, sum those as numbers and set the original number to the
string of that number.&lt;/p&gt;</description></item><item><title>AlgoDaily 10: Binary Tree In-order Traversal</title><link>https://notestoself.dev/posts/algodaily-10-binary-tree-in-order-traversal/</link><pubDate>Sat, 05 May 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-10-binary-tree-in-order-traversal/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Can you write a function to traverse a binary tree in-order, and print out
the value of each node as it passes?&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 4
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; \
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; /
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 6
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;The example would output [4, 6, 5] since there is no left child for 4, and 6
is visited in-order before 5.&lt;/p&gt;
&lt;p&gt;The definition of a tree node is as follows:&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Another example tree:&lt;/p&gt;</description></item><item><title>Django model field choices with an inner class enum</title><link>https://notestoself.dev/posts/django-field-choices-inner-class-enum/</link><pubDate>Wed, 02 May 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/django-field-choices-inner-class-enum/</guid><description>&lt;p&gt;It&amp;rsquo;s common to define the possible
&lt;a href="https://docs.djangoproject.com/en/2.0/ref/models/fields/#choices"&gt;choices&lt;/a&gt; for
a character field on Django models like this (from the docs):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Student&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;FRESHMAN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;FR&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;SOPHOMORE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;SO&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;JUNIOR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;JR&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;SENIOR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;SR&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;YEAR_IN_SCHOOL_CHOICES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;FRESHMAN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Freshman&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SOPHOMORE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Sophomore&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JUNIOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Junior&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SENIOR&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;Senior&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;year_in_school&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;max_length&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;choices&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;YEAR_IN_SCHOOL_CHOICES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;FRESHMAN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This works fine and it makes constants available for the values, so that
elsewhere you can refer to &lt;code&gt;Student.FRESHMAN&lt;/code&gt;. It is quite verbose, though, and
doesn&amp;rsquo;t group the constants together very well.&lt;/p&gt;
&lt;p&gt;One way to group the constants more clearly is to use an inner class:&lt;/p&gt;</description></item><item><title>uMed</title><link>https://notestoself.dev/work/umed/</link><pubDate>Mon, 16 Apr 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/umed/</guid><description>&lt;p&gt;I was a back-end Software Engineer at uMed in 2018.&lt;/p&gt;</description></item><item><title>PHP Business Time library v1.0.0</title><link>https://notestoself.dev/posts/php-business-time-library-v1/</link><pubDate>Fri, 13 Apr 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/php-business-time-library-v1/</guid><description>&lt;p&gt;I&amp;rsquo;ve just released v1.0.0 of my [PHP Business Time](&lt;a href="https://github.com/hughgrigg"&gt;https://github.com/hughgrigg&lt;/a&gt;
/php-business-time) library. I made this to solve my own problem that I had
building my &lt;a href="https://www.poprobincards.co.uk/"&gt;3D pop-up card shop&lt;/a&gt; &amp;ndash;
calculating estimated delivery dates based on business time constraints.&lt;/p&gt;
&lt;p&gt;It would have been simpler to just solve the exact use case I had (probably
hard-coding timings and combining that with retrieving holidays from a remote
source), but I thought it would be fun to generalise it and make it into a
separate library.&lt;/p&gt;</description></item><item><title>Fixing a Github Action error "botocore.awsrequest.AWSRequest" "exit code 255"</title><link>https://notestoself.dev/posts/github-action-s3-botocore-awsrequest-process-255-error/</link><pubDate>Mon, 09 Apr 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/github-action-s3-botocore-awsrequest-process-255-error/</guid><description>&lt;p&gt;The other day I suddenly started hitting an error in my Github Actions that
&lt;a href="https://notestoself.dev/posts/deploy-hugo-site-github-actions-s3-cloudfront-aws-iam/"&gt;deploy static Hugo sites to S3&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;lt;botocore.awsrequest.AWSRequest object at 0x7fbfb444e160&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Error: Process completed with exit code 255.
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For some reason setting an env variable called &lt;code&gt;AWS_EC2_METADATA_DISABLED&lt;/code&gt; to
&lt;code&gt;true&lt;/code&gt; fixes this error.&lt;/p&gt;
&lt;p&gt;E.g. your Github Action YAML might look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Build and Deploy&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;push&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;build&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Build and Deploy&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;runs-on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;ubuntu-latest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;uses&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;actions/checkout@v1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Install Hugo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; HUGO_DOWNLOAD=hugo_extended_${HUGO_VERSION}_Linux-64bit.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; wget https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/${HUGO_DOWNLOAD}
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; tar xvzf ${HUGO_DOWNLOAD} hugo
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; mv hugo $HOME/hugo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HUGO_VERSION&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0.71.0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Hugo Build&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;$HOME/hugo -v&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;Deploy to S3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;if&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;github.ref == &amp;#39;refs/heads/master&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;run&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;aws s3 sync public/ s3://{s3-bucket-name}/ --delete --acl=public-read &amp;amp;&amp;amp; aws cloudfront create-invalidation --distribution-id={cloudfront-distribution-id} --paths=&amp;#39;/*&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.AWS_ACCESS_KEY_ID }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${{ secrets.AWS_SECRET_ACCESS_KEY }}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;AWS_EC2_METADATA_DISABLED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The key bit for avoiding this error is:&lt;/p&gt;</description></item><item><title>AlgoDaily 09: Implement a Hash Map</title><link>https://notestoself.dev/posts/algodaily-09-implement-a-hash-map/</link><pubDate>Fri, 06 Apr 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-09-implement-a-hash-map/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Can you implement the JS Map class from scratch? Only two methods are
necessary &amp;ndash; &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;set&lt;/code&gt;. Do not use JS Objects ({} notation) for this
exercise.&lt;/p&gt;
&lt;p&gt;For the two methods you&amp;rsquo;ll define:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;get(key: string)&lt;/code&gt; should be given a key, and return the value for that
key.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;set(key: string, val: string)&lt;/code&gt; should take a key and a value as
parameters, and store the pair.&lt;/p&gt;
&lt;p&gt;Additionally, we&amp;rsquo;ve supplied the below hashing function hashStr. It tries to
avoid collision, but is not perfect. It takes in a string value and returns an
integer.&amp;rdquo;&lt;/p&gt;</description></item><item><title>AlgoDaily 08: Lonely Number</title><link>https://notestoself.dev/posts/algodaily-08-lonely-number/</link><pubDate>Thu, 05 Apr 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-08-lonely-number/</guid><description>&lt;p&gt;&lt;a href="https://algodaily.com/challenges/lonely-number"&gt;https://algodaily.com/challenges/lonely-number&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;In a given array of numbers, one element will show up once and the rest will
show up twice. Can you find that number in O(n) linear time?&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;lonelyNumber&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 6
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;lonelyNumber&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 9
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This seems straightforward.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;lonelyNumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;			&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{}))[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;We start with an empty hash, then go through the array, removing an item we&amp;rsquo;ve
seen before, and adding an item we haven&amp;rsquo;t.&lt;/p&gt;</description></item><item><title>AlgoDaily 07: Power of Three</title><link>https://notestoself.dev/posts/algodaily-07-power-of-three/</link><pubDate>Tue, 03 Apr 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-07-power-of-three/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Given an integer num, write a method to determine if it is a power of 3.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;powerOfThree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// true
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To calculate a power of &lt;em&gt;x&lt;/em&gt;, we could keep multiplying by &lt;em&gt;x&lt;/em&gt; until we hit the
exponent.&lt;/p&gt;
&lt;p&gt;That means to see if something is a power of &lt;em&gt;x&lt;/em&gt;, we can do the reverse and keep
diving by &lt;em&gt;x&lt;/em&gt; until we hit &lt;em&gt;1&lt;/em&gt; or a number that is not a multiple of &lt;em&gt;3&lt;/em&gt;.&lt;/p&gt;</description></item><item><title>URL encoding data Etsy-style in Python</title><link>https://notestoself.dev/posts/url-encode-etsy-style-python/</link><pubDate>Mon, 26 Mar 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/url-encode-etsy-style-python/</guid><description>&lt;p&gt;The &lt;a href="https://www.etsy.com/developers/documentation/getting_started/api_basics"&gt;Etsy
API&lt;/a&gt;
has quite specific requirements for how data should be encoded in requests.&lt;/p&gt;
&lt;p&gt;With a bit of trial and error making test requests, I ended up with these Python
functions to encode data in the way the Etsy API requires:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;urllib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;etsy_urlencoded&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;parse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_urlencoded_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;etsy_urlencoded_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Union&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;float&lt;/span&gt;&lt;span class="p"&gt;]):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;etsy_urlencoded_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;()})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="nb"&gt;list&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="nb"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;,&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;etsy_urlencoded_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This lets us automate some of the management of our &lt;a href="https://momentwallart.co.uk/"&gt;wall art
prints&lt;/a&gt; in our Etsy shop.&lt;/p&gt;</description></item><item><title>Avoid deriving state from props in React</title><link>https://notestoself.dev/posts/react-avoid-derived-state-from-props/</link><pubDate>Fri, 23 Mar 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/react-avoid-derived-state-from-props/</guid><description>&lt;p&gt;A common &lt;a href="https://en.wiktionary.org/wiki/footgun"&gt;footgun&lt;/a&gt; in React projects is
&lt;a href="https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html"&gt;derived state&lt;/a&gt;,
which means basing state on props. This can be directly copying props into
state, or deriving state from props with some extra logic.&lt;/p&gt;
&lt;p&gt;This often causes trouble because once the state is set based on props, it won&amp;rsquo;t
be updated by subsequent updates to props. This is after all the purpose of
props and state in React: props are clean and stateless, and state is of course
stateful.&lt;/p&gt;</description></item><item><title>AlgoDaily 06: Majority Element</title><link>https://notestoself.dev/posts/algodaily-06-majority-element/</link><pubDate>Sat, 03 Mar 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-06-majority-element/</guid><description>&lt;p&gt;&lt;a href="https://algodaily.com/challenges/majority-element"&gt;https://algodaily.com/challenges/majority-element&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Suppose we&amp;rsquo;re given an array of numbers like the following:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;[4, 2, 4]&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Could you find the majority element? A majority is defined as &amp;ldquo;the greater
part, or more than half, of the total. It is a subset of a set consisting of
more than half of the set&amp;rsquo;s elements.&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s assume that the array length is always at least one, and that there&amp;rsquo;s
always a majority element.&lt;/p&gt;
&lt;p&gt;In the example above, the majority element would be 4.&amp;rdquo;&lt;/p&gt;</description></item><item><title>AlgoDaily 05: Validate Palindrome</title><link>https://notestoself.dev/posts/algodaily-05-validate-palindrome/</link><pubDate>Fri, 02 Mar 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-05-validate-palindrome/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Given a string str, can you write a method that will return True if is a
palindrome and False if it is not? If you&amp;rsquo;ll recall, a palindrome is defined
as &amp;ldquo;a word, phrase, or sequence that reads the same backward as forward&amp;rdquo;. For
now, assume that we will not have input strings that contain special
characters or spaces, so the following examples hold:&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;let str = &amp;#39;thisisnotapalindrome&amp;#39;;
isPalindrome(str);
// false

str = &amp;#39;racecar&amp;#39;;
isPalindrome(str);
// true
&lt;/code&gt;&lt;/pre&gt;&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;For an extra challenge, try to ignore non-alphanumerical characters. The
final solution that we present will handle all edge cases.&amp;rdquo;&lt;/p&gt;</description></item><item><title>AlgoDaily 04: Is An Anagram</title><link>https://notestoself.dev/posts/algodaily-04-is-an-anagram/</link><pubDate>Thu, 01 Mar 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-04-is-an-anagram/</guid><description>&lt;p&gt;&lt;a href="https://algodaily.com/challenges/is-an-anagram"&gt;https://algodaily.com/challenges/is-an-anagram&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Here&amp;rsquo;s the definition of an anagram: a word, phrase, or name formed by
rearranging the letters of another, such as cinema, formed from iceman.&lt;/p&gt;
&lt;p&gt;We are given two strings like &amp;ldquo;cinema&amp;rdquo; and &amp;ldquo;iceman&amp;rdquo; as inputs. Can you write a
method isAnagram(str1, str2) that will return true or false depending on
whether the strings are anagrams of each other?&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The gotcha with this is being careful with multiple instances of the same
letter, i.e. you can&amp;rsquo;t just check that each character is also found in the other
string.&lt;/p&gt;</description></item><item><title>AlgoDaily 03: Reverse Only Alphabetical</title><link>https://notestoself.dev/posts/algodaily-03-reverse-only-alphabetical/</link><pubDate>Tue, 20 Feb 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-03-reverse-only-alphabetical/</guid><description>&lt;p&gt;(&lt;a href="https://algodaily.com/challenges/reverse-only-alphabetical)%5Bhttps://algodaily.com/challenges/reverse-only-alphabetical%5D"&gt;https://algodaily.com/challenges/reverse-only-alphabetical)[https://algodaily.com/challenges/reverse-only-alphabetical]&lt;/a&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;You are given a string that contains alphabetical characters (a - z, A - Z)
and some other characters ($, !, etc.). For example, one input may be:&lt;/p&gt;
&lt;p&gt;&amp;lsquo;sea!$hells3&amp;rsquo;&lt;/p&gt;
&lt;p&gt;Can you reverse only the alphabetical ones?&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;First thought is to use a stack for this: go through the string forwards,
pushing only letters on to the stack. Then go through the positions again,
popping the stack for a letter position but leaving non-letters alone:&lt;/p&gt;</description></item><item><title>AlgoDaily 02: Fizz Buzz</title><link>https://notestoself.dev/posts/algodaily-02-fizz-buzz/</link><pubDate>Mon, 19 Feb 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-02-fizz-buzz/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;We&amp;rsquo;re given a number &lt;em&gt;n&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Write a function that returns the string representation of all numbers from 1
to n based on the following rules:&lt;/p&gt;
&lt;p&gt;If it&amp;rsquo;s a multiple of 3, represent it as &amp;ldquo;fizz&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;If it&amp;rsquo;s a multiple of 5, represent it as &amp;ldquo;buzz&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;If it&amp;rsquo;s a multiple of both 3 and 5, represent it as &amp;ldquo;fizzbuzz&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;If it&amp;rsquo;s neither, just return the number itself.&lt;/p&gt;
&lt;p&gt;As such, fizzBuzz(15) would result in &amp;lsquo;12fizz4buzzfizz78fizzbuzz11fizz1314fizzbuzz&amp;rsquo;.&amp;rdquo;&lt;/p&gt;</description></item><item><title>AlgoDaily 01: Array Intersection</title><link>https://notestoself.dev/posts/algodaily-01-array-intersection/</link><pubDate>Sun, 18 Feb 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algodaily-01-array-intersection/</guid><description>&lt;p&gt;I&amp;rsquo;ve signed up to &lt;a href="https://algodaily.com/"&gt;AlgoDaily&lt;/a&gt; as it seems like a fun way
to get out some small blog posts on small coding tasks.&lt;/p&gt;
&lt;p&gt;This is &lt;em&gt;AlgoDaily Day 1: Array Intersection&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Can you write a function that takes two inputs and returns their
intersection? All element in the final result should be unique.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nums1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nums2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;intersection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nums1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nums2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// [2]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;Here&amp;rsquo;s another one:&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nums1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nums2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;intersection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nums1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nums2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// [9, 4]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Here&amp;rsquo;s my first-thought naive implementation:&lt;/p&gt;</description></item><item><title>Varmilo keyboard F-keys and Super Win key mode on Ubuntu</title><link>https://notestoself.dev/posts/ubuntu-varmilo-keyboard-f-super-win-key-mode/</link><pubDate>Thu, 15 Feb 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/ubuntu-varmilo-keyboard-f-super-win-key-mode/</guid><description>&lt;p&gt;I got a &lt;a href="https://www.keyboardco.com/keyboard/uk-va109m-cmyk-pbt-backlit-mx-brown-tactile-keyboard.asp"&gt;Varmilo
VA109M&lt;/a&gt;
keyboard recently (it&amp;rsquo;s great!), which I&amp;rsquo;m using on Ubuntu 20.10.&lt;/p&gt;
&lt;p&gt;This is how I got the F-keys working correctly:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; options hid_apple &lt;span class="nv"&gt;fnmode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; sudo tee -a /etc/modprobe.d/hid_apple.conf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo update-initramfs -u -k all
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then restart.&lt;/p&gt;
&lt;p&gt;And to get the Super key (labelled &lt;code&gt;Win&lt;/code&gt; on Varmilo PC keyboards) working
correctly, make sure your Varmilo keyboard is in PC mode:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hold down &lt;code&gt;Fn + Esc&lt;/code&gt; for 4 seconds.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;CapsLock&lt;/code&gt; key light will flash when the mode has been changed successfully.&lt;/p&gt;</description></item><item><title>Cartesian products in Bash</title><link>https://notestoself.dev/posts/cartesian-product-bash/</link><pubDate>Tue, 23 Jan 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/cartesian-product-bash/</guid><description>&lt;p&gt;Here&amp;rsquo;s a handy thing you can do in Bash: calculate the
&lt;a href="https://en.wikipedia.org/wiki/Cartesian_product"&gt;Cartesian product&lt;/a&gt; of two
sets.&lt;/p&gt;
&lt;p&gt;E.g.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;A,B,C&lt;span class="o"&gt;}{&lt;/span&gt;A,B,C&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;gives&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;AA AB AC BA BB BC CA CB CC
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;i.e. it&amp;rsquo;s every possible permutation of two elements from the two sets.&lt;/p&gt;
&lt;pre class="text-diagram"&gt;
A --- A
 \ /
B -x- B
 / \
C --- C
&lt;/pre&gt;


&lt;p&gt;(Thanks to &lt;a href="http://www.billhails.net/"&gt;Bill Hails&lt;/a&gt; for the diagram!)&lt;/p&gt;
&lt;p&gt;This can be a useful tool for quickly figuring things out in various situations.
For example, I wanted to write some tests for a feature that allowed users to
set two configuration options to one of three values. Bash made it easy to list
out the possible permutations I needed to cover in the tests.&lt;/p&gt;</description></item><item><title>Finding if two rectangles overlap in JavaScript</title><link>https://notestoself.dev/posts/finding-if-two-rectangles-overlap-in-javascript/</link><pubDate>Thu, 18 Jan 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/finding-if-two-rectangles-overlap-in-javascript/</guid><description>&lt;p&gt;Given the top-left and bottom-right coordinates of two rectangles, determine if
they overlap or not. That is, determine if any part of one rectangle overlaps
with any part of the other.&lt;/p&gt;
&lt;p&gt;The grid has position &lt;code&gt;(0, 0)&lt;/code&gt; at the top left. X increases rightwards and Y
increases downwards. This is common in 2D grids in video games.&lt;/p&gt;
&lt;pre class="text-diagram"&gt;

 0,0 ─ x →

 │

 y

 ↓

&lt;/pre&gt;


&lt;p&gt;You get coordinates for the rectangles as tuples of x and y, e.g. &lt;code&gt;(2, 5)&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Converting Roman numerals into decimal in JavaScript</title><link>https://notestoself.dev/posts/converting-roman-numerals-into-decimal-in-javascript/</link><pubDate>Mon, 08 Jan 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/converting-roman-numerals-into-decimal-in-javascript/</guid><description>&lt;p&gt;Converting Roman numerals into decimal is a fun little problem to solve. Getting
the solution is dependent on knowing the rules of Roman numerals, and they&amp;rsquo;re
actually simpler than you might think:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All letters directly map to an integer value.&lt;/li&gt;
&lt;li&gt;A letter followed by a letter of higher value is subtracted.&lt;/li&gt;
&lt;li&gt;All other letters are added.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Before I looked this up, I thought that the rules were more complicated, e.g.
with multiplication in certain situations (which is how Chinese numerals work).&lt;/p&gt;</description></item><item><title>Identifying a potential palindrome in JavaScript</title><link>https://notestoself.dev/posts/identifying-a-potential-palindrome-in-javascript/</link><pubDate>Fri, 05 Jan 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/identifying-a-potential-palindrome-in-javascript/</guid><description>&lt;p&gt;Given an input string, determine whether the string could possibly be a
palindrome if rearranged. It is not necessary to return the potential
palindrome, just to identify whether one is possible.&lt;/p&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;aabb =&amp;gt; true
abc =&amp;gt; false
aab =&amp;gt; true
ciicv =&amp;gt; true
aba =&amp;gt; true
abac =&amp;gt; false
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A palindrome can have any amount of pairs of letters, and optionally one single
odd letter with no partner in the middle.&lt;/p&gt;</description></item><item><title>Bloodchild</title><link>https://notestoself.dev/reading/bloodchild/</link><pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/bloodchild/</guid><description/></item><item><title>Dawn</title><link>https://notestoself.dev/reading/dawn/</link><pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/dawn/</guid><description/></item><item><title>Dune</title><link>https://notestoself.dev/reading/dune/</link><pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/dune/</guid><description/></item><item><title>How to Destroy a Tech Startup</title><link>https://notestoself.dev/reading/how-to-destroy-a-tech-startup/</link><pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/how-to-destroy-a-tech-startup/</guid><description/></item><item><title>Rationality: From AI to Zombies</title><link>https://notestoself.dev/reading/rationality-from-ai-to-zombies/</link><pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/rationality-from-ai-to-zombies/</guid><description/></item><item><title>The Big Short</title><link>https://notestoself.dev/reading/the-big-short/</link><pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-big-short/</guid><description/></item><item><title>The Daily Stoic</title><link>https://notestoself.dev/reading/the-daily-stoic/</link><pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-daily-stoic/</guid><description/></item><item><title>The Gervais Principle</title><link>https://notestoself.dev/reading/gervais-principle/</link><pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/gervais-principle/</guid><description/></item><item><title>The Heart to Start</title><link>https://notestoself.dev/reading/heart-to-start/</link><pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/heart-to-start/</guid><description/></item><item><title>The Inner Game of Tennis</title><link>https://notestoself.dev/reading/inner-game-of-tennis/</link><pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/inner-game-of-tennis/</guid><description/></item><item><title>The Spy Who Came In from the Cold</title><link>https://notestoself.dev/reading/spy-who-came-in-from-the-cold/</link><pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/spy-who-came-in-from-the-cold/</guid><description/></item><item><title>War is a Racket</title><link>https://notestoself.dev/reading/war-is-a-racket/</link><pubDate>Mon, 01 Jan 2018 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/war-is-a-racket/</guid><description/></item><item><title>Python Tricks</title><link>https://notestoself.dev/reading/python-tricks/</link><pubDate>Sun, 17 Dec 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/python-tricks/</guid><description/></item><item><title>Product Page model abstraction in e-commerce</title><link>https://notestoself.dev/posts/e-commerce-model-product-page-abstraction/</link><pubDate>Fri, 08 Dec 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/e-commerce-model-product-page-abstraction/</guid><description>&lt;p&gt;A lot of e-commerce abstractions get a bit messy around abstracting products,
SKUs, product options, product variations and so on. The fact that there doesn&amp;rsquo;t
seem to be a standard or obvious abstraction for this suggests it is tricky to
get right.&lt;/p&gt;
&lt;p&gt;It might be helpful in a lot of systems to extract a &lt;em&gt;Product Page&lt;/em&gt; model as an
abstraction. I think this avoids a lot of the trouble as it&amp;rsquo;s easy to think
about and &lt;a href="https://notestoself.dev/posts/code-like-conversations/"&gt;lets your code match your conversations&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Balancing address lines for e-commerce in PHP</title><link>https://notestoself.dev/posts/php-balance-address-lines-ecommerce/</link><pubDate>Thu, 07 Dec 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/php-balance-address-lines-ecommerce/</guid><description>&lt;p&gt;Delivery addresses are &lt;a href="https://github.com/kdeldycke/awesome-falsehood#postal-addresses"&gt;notoriously
difficult&lt;/a&gt; to
deal with. One issue we&amp;rsquo;ve encountered with our &lt;a href="https://www.poprobincards.co.uk/" title="3D pop up cards"&gt;greetings card
shop&lt;/a&gt; is long lines in an
address that would get cut off when printed on the address label, so we have to
manually tweak them to get the address to fit.&lt;/p&gt;
&lt;p&gt;To reduce some of that manual work, I added a little helper that tries to
balance the address lines where possible, i.e. take words off long lines and add
them to neighbouring lines that have some space to spare.&lt;/p&gt;</description></item><item><title>Fluent Python</title><link>https://notestoself.dev/reading/fluent-python/</link><pubDate>Tue, 05 Dec 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/fluent-python/</guid><description/></item><item><title>Drawing sequentially represented binary trees in Python</title><link>https://notestoself.dev/posts/draw-sequential-binary-tree-python/</link><pubDate>Thu, 23 Nov 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/draw-sequential-binary-tree-python/</guid><description>&lt;p&gt;I&amp;rsquo;ve been playing around with sequentially represented binary trees recently,
i.e. a binary tree given as a list of integers where the node values are in
contiguous sections of the list.&lt;/p&gt;
&lt;p&gt;For example, a binary tree where the node values are also their indexes would be
sequentially represented like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;0 | 1, 2 | 3, 4, 5, 6 | 7, 8, 9, 10, 11, 12, 13, 14
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each section in the list represents one level of the binary tree, and has length
$2^{l}$ where $l$ is the level. At the root, $l=0$ and $2^0=1$, so we know this
section has a length of 1. The next section represents $l=1$, so has length
$2^1=2$, and so on. At the fourth level, $l=3$, so there are $2^3=8$ nodes in
that section.&lt;/p&gt;</description></item><item><title>Managing Humans</title><link>https://notestoself.dev/reading/managing-humans/</link><pubDate>Thu, 05 Oct 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/managing-humans/</guid><description/></item><item><title>On the Trail of Genghis Kahn</title><link>https://notestoself.dev/reading/on-the-trail-of-genghis-kahn/</link><pubDate>Sun, 01 Oct 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/on-the-trail-of-genghis-kahn/</guid><description/></item><item><title>The Dispossessed</title><link>https://notestoself.dev/reading/the-dispossessed/</link><pubDate>Tue, 05 Sep 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-dispossessed/</guid><description>Shevek, a brilliant physicist, decides to take action. he will seek answers, question the unquestionable, and attempt to tear down the walls of hatred that have isolated his planet of anarchists from the rest of the civilized universe. To do this dangerous task will mean giving up his family and possibly his life. Shevek must make the unprecedented journey to the utopian mother planet, Anarres, to challenge the complex structures of life and living, and ignite the fires of change.</description></item><item><title>Blackjack CLI game</title><link>https://notestoself.dev/posts/blackjack-cli-game/</link><pubDate>Mon, 28 Aug 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/blackjack-cli-game/</guid><description>&lt;p&gt;The first draft of a &lt;a href="https://github.com/hughgrigg/blackjack"&gt;blackjack cli
game&lt;/a&gt; I&amp;rsquo;m making is now finished.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://notestoself.dev/img/2017/blackjack-01.png" alt="Blackjack screenshot"&gt;&lt;/p&gt;
&lt;p&gt;If you have Go installed then you should be able to play the game with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;go get -u github.com/hughgrigg/blackjack
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;blackjack
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s a single-deck blackjack betting game written in Go for the command line.
I&amp;rsquo;ve only tried it on Linux, but it uses the &lt;a href="https://github.com/gizak/termui"&gt;termui
library&lt;/a&gt; so I imagine it would be OK on OSX as
well. Not sure about Windows.&lt;/p&gt;</description></item><item><title>Enable PHP assertion errors on Laravel Homestead for development and testing</title><link>https://notestoself.dev/posts/enable-php-assertion-errors-on-laravel-homestead-for-development-and-testing/</link><pubDate>Wed, 23 Aug 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/enable-php-assertion-errors-on-laravel-homestead-for-development-and-testing/</guid><description>&lt;p&gt;Laravel&amp;rsquo;s &lt;a href="https://laravel.com/docs/7.x/homestead"&gt;Homestead&lt;/a&gt; vagrant setup is
handy to get going quickly on projects and keep them consistent to work on.&lt;/p&gt;
&lt;p&gt;I was surprised that it has PHP assertion errors disabled by default. It&amp;rsquo;s good
to have these enabled for local development and testing so that you can catch
more problems earlier.&lt;/p&gt;
&lt;p&gt;To enable PHP assertion errors in Homestead you can run this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; phpConf in cli fpm&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sudo sed -i &lt;span class="s1"&gt;&amp;#39;s/zend.assertions = -1/zend.assertions = 1/g&amp;#39;&lt;/span&gt; /etc/php/7.*/&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;phpConf&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/php.ini
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sudo sed -i &lt;span class="s1"&gt;&amp;#39;s/;assert.active = On/assert.active = On/g&amp;#39;&lt;/span&gt; /etc/php/7.*/&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;phpConf&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/php.ini
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; sudo sed -i &lt;span class="s1"&gt;&amp;#39;s/;assert.exception = On/assert.exception = On/g&amp;#39;&lt;/span&gt; /etc/php/7.*/&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;phpConf&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;/php.ini
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That will enable PHP assertions and assertion errors for cli and web for all
versions of PHP 7 in Homestead. I have it on a script so that it&amp;rsquo;s always run
and I won&amp;rsquo;t forget about it.&lt;/p&gt;</description></item><item><title>Doughnut Economics</title><link>https://notestoself.dev/reading/doughnut-economics/</link><pubDate>Sat, 05 Aug 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/doughnut-economics/</guid><description/></item><item><title>Dates for booleans</title><link>https://notestoself.dev/posts/dates-for-booleans/</link><pubDate>Sun, 23 Jul 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/dates-for-booleans/</guid><description>&lt;p&gt;Here&amp;rsquo;s a little maxim for data modeling: &lt;strong&gt;use dates instead of booleans&lt;/strong&gt;. In a
lot of situations, this lets you capture more information without losing
anything compared to a boolean.&lt;/p&gt;
&lt;p&gt;A common example is implementing a status column with a datetime &lt;code&gt;foobared_at&lt;/code&gt;
column instead of a boolean &lt;code&gt;is_foobar&lt;/code&gt;. If it&amp;rsquo;s null, then that row is not
foobar. If the column is filled, then you not only know that it is foobar, but
also since when.&lt;/p&gt;</description></item><item><title>Early exit boolean expression parser in Python</title><link>https://notestoself.dev/posts/python-early-exit-boolean-expression-parser/</link><pubDate>Tue, 18 Jul 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/python-early-exit-boolean-expression-parser/</guid><description>&lt;p&gt;I wanted to have a go at rush-implementing a boolean expression parser with
early exit, i.e. that it will not bother evaluating the right hand side of an
expression like &lt;code&gt;0&amp;amp;(1|0)&lt;/code&gt; and just return &lt;code&gt;false&lt;/code&gt; once it sees &lt;code&gt;0&amp;amp;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an attempt in Python. It deals with nested expressions by recursion and
&amp;ldquo;collapsing&amp;rdquo; the evaluation into a &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;1&lt;/code&gt; token to continue evaluating from
there. It uses a similar approach to handle &lt;code&gt;!&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Superforecasting</title><link>https://notestoself.dev/reading/superforecasting/</link><pubDate>Wed, 05 Jul 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/superforecasting/</guid><description/></item><item><title>Nordic APIs OAuth Workshop, Amsterdam June 2017</title><link>https://notestoself.dev/posts/nordic-apis-oauth-workshop/</link><pubDate>Mon, 12 Jun 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/nordic-apis-oauth-workshop/</guid><description>&lt;p&gt;In June I attended [Nordic API&amp;rsquo;s workshop](&lt;a href="http://nordicapis.com/events/nordic-"&gt;http://nordicapis.com/events/nordic-&lt;/a&gt;
apis-workshops-amsterdam/) on OAuth and OpenID in Amsterdam. Here are my notes
on the class.&lt;/p&gt;
&lt;h2 id="actors-in-oauth"&gt;Actors in OAuth&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;AS&lt;/strong&gt;: Authorisation Server (OAuth Server), e.g. bank auth server&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RS&lt;/strong&gt;: Resource Server, e.g. bank account server&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RO&lt;/strong&gt;: Resource Owner, e.g. bank customer&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;C&lt;/strong&gt;: Client, e.g. mobile app&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OAuth is designed for cross-organisational authorisation, i.e. where the client
is not trusted (which should always be the case). The AS and RO do not want to
let the client nor the resource server have access to the RO&amp;rsquo;s credentials.&lt;/p&gt;</description></item><item><title>The Corrections</title><link>https://notestoself.dev/reading/the-corrections/</link><pubDate>Mon, 05 Jun 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-corrections/</guid><description/></item><item><title>Auto-printing shipping labels with Laravel, Raspberry Pi and AWS</title><link>https://notestoself.dev/posts/label-print-laravel-raspberry-pi-aws/</link><pubDate>Mon, 29 May 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/label-print-laravel-raspberry-pi-aws/</guid><description>&lt;p&gt;The most manual part of the e-commerce &lt;a href="https://notestoself.dev/work/poprobincards/"&gt;side project&lt;/a&gt; I
run with my wife is shipping the orders. We get order notifications by [email
and Telegram](&lt;a href="https://github.com/hughgrigg/poprobincards/bl"&gt;https://github.com/hughgrigg/poprobincards/bl&lt;/a&gt;
ob/a08850cbee203223db309965b6e264e7e951e693/app/Modules/Sales/Notifications/Staf
fOrderNotification.php#L52), and have to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Find the stock.&lt;/li&gt;
&lt;li&gt;Package it.&lt;/li&gt;
&lt;li&gt;Print the shipping label, cut out and attach it.&lt;/li&gt;
&lt;li&gt;Take the order to the post box.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A lot of that is beyond our means to automate, but we did get a &lt;a href="https://www.brother.co.uk/labelling/ql-printers/ql570"&gt;Brother
QL-570&lt;/a&gt; label printer to
speed up step 3 quite a lot. That prints and cuts adhesive labels that look
more professional than what we were doing before, and costs less per label.&lt;/p&gt;</description></item><item><title>Algorithm Design Exercise 1-29: Finding the Fastest of 25 Horses</title><link>https://notestoself.dev/posts/algorithm-design-exercise-1-29/</link><pubDate>Thu, 25 May 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algorithm-design-exercise-1-29/</guid><description>&lt;p&gt;This is exercise 1-29 from &lt;em&gt;The Algorithm Design Manual&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There are 25 horses. At most, 5 horses can race together at a time. You must
determine the fastest, second fastest, and third fastest horses. Find the
minimum number of races in which this can be done.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is quite a famous puzzle question and involves some algorithmic thinking.&lt;/p&gt;
&lt;p&gt;The first key to understanding for me was to focus on &lt;em&gt;excluding horses who are
slower than at least 3 other horses&lt;/em&gt;. This lets you run a process of elimination
instead of a process of selection.&lt;/p&gt;</description></item><item><title>Survivorship Thinking</title><link>https://notestoself.dev/posts/survivorship-thinking/</link><pubDate>Tue, 23 May 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/survivorship-thinking/</guid><description>&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Survivorship_bias"&gt;survivorship bias&lt;/a&gt; is one
of the most interesting cognitive biases to know about (&lt;a href="https://xkcd.com/1827/"&gt;cliché XKCD
link&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s the idea that our impression of a given set of things is biased towards
those that have survived some selection pressure. The classic example is when
people say &amp;ldquo;they don&amp;rsquo;t make &amp;rsquo;em like they used to&amp;rdquo;. With a moment&amp;rsquo;s thought, you
realise that of course all the old things we see today are well made. The
poorly-made things of the past don&amp;rsquo;t survive through to the present.&lt;/p&gt;</description></item><item><title>Rendering a comma-separated list of items in a Hugo template with a dot after the last</title><link>https://notestoself.dev/posts/hugo-template-list-comma-separated-last-full-stop/</link><pubDate>Sat, 13 May 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/hugo-template-list-comma-separated-last-full-stop/</guid><description>&lt;p&gt;Here&amp;rsquo;s a Hugo template snippet for rendering a list of items separated by a
comma and space after all but the last item, and then a dot or full stop.&lt;/p&gt;
&lt;p&gt;For example, if you have a tags taxonomy and some tags in a post&amp;rsquo;s front-matter
like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;Foo&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;Bar&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;Baz&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nn"&gt;---&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You can render a comma-separated list of those tags in a template like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ with (.GetTerms &amp;#34;tags&amp;#34;) }}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; {{ range $i, $t := . }}{{ if $i }},&lt;span class="ni"&gt;&amp;amp;nbsp;&lt;/span&gt;{{ end }}&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;{{ $t.Permalink }}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ $t.LinkTitle }}&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;a&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;{{ end }}.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;{{ end }}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;$i&lt;/code&gt; variable is the numeric range index and the &lt;code&gt;$t&lt;/code&gt; variable is the tag
object per tag. This uses the false evaluation of the first index &lt;code&gt;0&lt;/code&gt; to add a
comma and space &amp;ldquo;&lt;code&gt;,&amp;amp;nbsp;&lt;/code&gt;&amp;rdquo; after all but the first item in the range.&lt;/p&gt;</description></item><item><title>"Permission denied (public key)" error from Laravel Forge after copying ssh key using xclip or pbcopy</title><link>https://notestoself.dev/posts/laravel-forge-ssh-key-xclip-pbcopy-permission-denied-public-key/</link><pubDate>Fri, 12 May 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/laravel-forge-ssh-key-xclip-pbcopy-permission-denied-public-key/</guid><description>&lt;p&gt;I use &lt;a href="https://forge.laravel.com/"&gt;Laravel Forge&lt;/a&gt; to run several Laravel
projects. It has an interface to add SSH keys to a server so that you can SSH
into them, but I&amp;rsquo;ve repeatedly hit a little issue when trying to use xclip on
Linux or pbcopy on Mac to copy the public key and paste it into the Forge
interface.&lt;/p&gt;
&lt;p&gt;Somehow the public key is not copied properly and you&amp;rsquo;ll get the generic
&lt;code&gt;Permission denied (public key)&lt;/code&gt; error when trying to SSH into the server.&lt;/p&gt;</description></item><item><title>The Every Computer Performance Book</title><link>https://notestoself.dev/reading/the-every-computer-performance-book/</link><pubDate>Mon, 01 May 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-every-computer-performance-book/</guid><description/></item><item><title>The Remains of the Day</title><link>https://notestoself.dev/reading/the-remains-of-the-day/</link><pubDate>Mon, 01 May 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-remains-of-the-day/</guid><description/></item><item><title>Algorithm Design Exercise 1-28</title><link>https://notestoself.dev/posts/algorithm-design-exercise-1-28/</link><pubDate>Sun, 23 Apr 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algorithm-design-exercise-1-28/</guid><description>&lt;p&gt;This is exercise 1-28 from &lt;em&gt;The Algorithm Design Manual&lt;/em&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Write a function to perform integer division without using either the / or *
operators. Find a fast way to do it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&amp;rsquo;s add multiplication as well. We could do a very inefficient multiplication
algorithm by using one factor as a counter, and incrementing the other factor by
itself until the counter reaches its value:&lt;/p&gt;
&lt;pre class="tex2jax_process mjx-block"&gt;
$a \times b$
$i = 0$
$p = 0$
while $i &lt; a$
	$p = p + b$
	$i = i + 1$
return $p$
&lt;/pre&gt;

&lt;p&gt;&lt;a href="https://notestoself.dev/code/adm/1/naive-multiply.py"&gt;Naive integer multiplication in Python&lt;/a&gt;&lt;/p&gt;</description></item><item><title>Station Eleven</title><link>https://notestoself.dev/reading/station-eleven/</link><pubDate>Sat, 01 Apr 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/station-eleven/</guid><description>An audacious, darkly glittering novel about art, fame, and ambition set in the eerie days of civilization&amp;rsquo;s collapse, from the author of three highly acclaimed previous novels. One snowy night a famous Hollywood actor slumps over and dies onstage during a production of King Lear. Hours later, the world as we know it begins to dissolve. Moving back and forth in time-from the actor&amp;rsquo;s early days as a film star to fifteen years in the future, when a theater troupe known as the Traveling Symphony roams the wasteland of what remains-this suspenseful, elegiac, spellbinding novel charts the strange twists of fate that connect five people: the actor, the man who tried to save him, the actor&amp;rsquo;s first wife, his oldest friend, and a young actress with the TravelingSymphony, caught in the crosshairs of a dangerous self-proclaimed prophet. Sometimes terrifying, sometimes tender, Station Eleven tells a story about the relationships that sustain us, the ephemeral nature of fame, and the beauty of the world as we know it&amp;ndash;</description></item><item><title>Stripping unprintable Unicode variation characters in PHP</title><link>https://notestoself.dev/posts/stripping-unprintable-unicode-variation-characters-in-php/</link><pubDate>Fri, 31 Mar 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/stripping-unprintable-unicode-variation-characters-in-php/</guid><description>&lt;p&gt;Emoji characters like 🥳, 🎉 and 💖 can be represented in a string in different
ways. The first way is to include a Unicode point as a single character. For
example 💖 is Unicode point &lt;code&gt;U+1F496&lt;/code&gt;, which can be included in a PHP string
either directly (&lt;code&gt;'💖'&lt;/code&gt;) or using the &lt;a href="https://www.php.net/manual/en/migration70.new-features.php#migration70.new-features.unicode-codepoint-escape-syntax"&gt;Unicode code point escape
syntax&lt;/a&gt;
from PHP 7 onwards: &lt;code&gt;&amp;quot;\u{1F496}&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can try these on the PHP REPL (&lt;code&gt;php -a&lt;/code&gt;):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;php&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;💖&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;💖&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;php&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;\u{1F496}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;💖&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Emoji characters might also be represented using a &lt;a href="https://en.wikipedia.org/wiki/Zero-width_joiner"&gt;zero width
joiner&lt;/a&gt; Unicode point, which
lets you combine two separate Unicode points together to be displayed as a
single character. For example, the rainbow flag emoji 🏳️‍🌈 can be produced by
the sequence &lt;code&gt;[Waving white flag] [ZWJ] [Rainbow]&lt;/code&gt; (&lt;a href="http://xahlee.info/comp/unicode_flags.html"&gt;flags are a common use-case
for the ZWJ character&lt;/a&gt;).&lt;/p&gt;</description></item><item><title>Algorithm Design Exercises 1</title><link>https://notestoself.dev/posts/algorithm-design-1/</link><pubDate>Thu, 23 Mar 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/algorithm-design-1/</guid><description>&lt;p&gt;These are the exercises for chapter 1 of &lt;em&gt;The Algorithm Design Manual&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id="1-1"&gt;1-1&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Show that $a + b$ can be less than $min(a,b)$&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Where $a,b &amp;lt; 0$, e.g. if $a = -1, b = -2$, then $a + b = -3 &amp;lt; min(a,b) = -5$.&lt;/p&gt;
&lt;h2 id="1-2"&gt;1-2&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Show that $a \times b$ can be less than $min(a,b)$&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Where one of $\{a,b\}$ is negative and one is positive, e.g. if $a = -1, b =
2$, then $a \times b = -2 &amp;lt; min(a,b) = -1$.&lt;/p&gt;</description></item><item><title>Cracking the Coding Interview 6thE 10.1 Sorted Merge</title><link>https://notestoself.dev/posts/cracking-coding-interview-6th-10.1-sorted-merge/</link><pubDate>Thu, 23 Mar 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/cracking-coding-interview-6th-10.1-sorted-merge/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;&lt;strong&gt;Sorted Merge&lt;/strong&gt;: You are given two sorted arrays, A and B, where A has a
large enough buffer at the end to hold B. Write a method to merge B into A in
sorted order.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Arrays are already sorted, so we can just do an insertion sort: iterate both,
taking the next sorted item from each and insert it into A which has space for
both.&lt;/p&gt;
&lt;p&gt;It would be tricky to insert at the beginning, though, as we&amp;rsquo;d have to shift the
existing elements in A. So we can do the insertion sort in reverse order, taking
the largest element at a time from the end of each array and adding it to the
moving end of A.&lt;/p&gt;</description></item><item><title>Uploading a listing image to the Etsy API in Python</title><link>https://notestoself.dev/posts/etsy-api-listing-image-upload-python/</link><pubDate>Tue, 21 Mar 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/etsy-api-listing-image-upload-python/</guid><description>&lt;p&gt;It took a bit of messing around, but I was able to upload product listing images
via the Etsy API in Python 3 with &lt;code&gt;requests_oauthlib&lt;/code&gt; like this (reduced to the
essentials):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;requests_oauthlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;OAuth1Session&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;OAuth1Session&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;{etsy client key}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;client_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{etsy client secret}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;resource_owner_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{etsy resource owner key}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;resource_owner_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;{etsy resource owner secret}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;/listings/123456789/images&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;listing_id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;123456789&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;rank&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;overwrite&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;is_watermarked&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;image&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;image123.jpg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;image123.jpg&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;rb&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;image/jpeg&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;I&amp;rsquo;ve been using this to automate management of the art decor images for our
&lt;a href="https://momentwallart.co.uk/"&gt;wall art prints shop&lt;/a&gt;.&lt;/p&gt;</description></item><item><title>Getting started with Teach Yourself CS: Algorithms and Data Structures</title><link>https://notestoself.dev/posts/getting-started-teach-yourself-cs-algorithms-data-structures/</link><pubDate>Fri, 17 Mar 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/getting-started-teach-yourself-cs-algorithms-data-structures/</guid><description>&lt;p&gt;The website &lt;a href="https://teachyourselfcs.com/"&gt;TeachYourselfCS.com&lt;/a&gt; was recently
featured on Hacker News, and after reading through I&amp;rsquo;ve decided to embark on a
new learning journey by going through one of its subjects as properly as I can.&lt;/p&gt;
&lt;p&gt;The subject I&amp;rsquo;ve chosen is &lt;a href="https://teachyourselfcs.com/#algorithms"&gt;Algorithms and Data
Structures&lt;/a&gt;, as I think that will be
most interesting to me personally, and will have the most immediate practical
application in the kinds of things I like to do.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m very grateful that the recommended lecture series, &lt;a href="https://www3.cs.stonybrook.edu/~algorith/video-lectures/"&gt;Skiena&amp;rsquo;s Algorithms
Lectures&lt;/a&gt;, is freely
available online (&lt;a href="https://www.youtube.com/watch?v=ZFjhkohHdAA&amp;amp;list=PLOtl7M3yp-DV69F32zdK7YJcNXpTunF2b"&gt;YouTube copy&lt;/a&gt;), as are &lt;a href="https://www3.cs.stonybrook.edu/~skiena/teaching/"&gt;course
documents&lt;/a&gt; for that course and
various others. I was also able to find a copy of &lt;em&gt;The Algorithm Design Manual&lt;/em&gt;
with a quick search.&lt;/p&gt;</description></item><item><title>Deep Survival</title><link>https://notestoself.dev/reading/deep-survival/</link><pubDate>Wed, 01 Mar 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/deep-survival/</guid><description/></item><item><title>Algorithms to Live By</title><link>https://notestoself.dev/reading/algorithms-to-live-by/</link><pubDate>Wed, 01 Feb 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/algorithms-to-live-by/</guid><description/></item><item><title>Moment Wall Art</title><link>https://notestoself.dev/work/moment-wall-art/</link><pubDate>Mon, 23 Jan 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/moment-wall-art/</guid><description/></item><item><title>Creating &amp; updating inventory product offerings via the Etsy API in Python</title><link>https://notestoself.dev/posts/etsy-api-create-update-inventory-product-offerings/</link><pubDate>Sun, 08 Jan 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/etsy-api-create-update-inventory-product-offerings/</guid><description>&lt;p&gt;Managing products and product variations in the Etsy API can be a bit tricky,
especially when you want to &amp;ldquo;sync&amp;rdquo; them, i.e. either create or update in the
Etsy API according to your own product data.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s an approach in Python that I got working.&lt;/p&gt;
&lt;p&gt;Say your product data looks like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;my_products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;sku_123&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;style&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;foo&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;price&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;15.00&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;sku_456&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;style&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;bar&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;price&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;25.00&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;quantity&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;One approach is to fetch the existing Etsy listing&amp;rsquo;s &amp;ldquo;inventory&amp;rdquo; (which might be
empty for a newly created one), update it to match your local data, and then put
it back to the Etsy API.&lt;/p&gt;</description></item><item><title>A History of Modern Britain</title><link>https://notestoself.dev/reading/a-history-of-modern-britain/</link><pubDate>Sun, 01 Jan 2017 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/a-history-of-modern-britain/</guid><description/></item><item><title>Simplicity is a skill</title><link>https://notestoself.dev/posts/simplicity-is-a-skill/</link><pubDate>Sun, 23 Oct 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/simplicity-is-a-skill/</guid><description>&lt;p&gt;After a lot of peer reviews and looking back at my own work recently, I&amp;rsquo;ve been
considering the point I was trying to get across in &amp;ldquo;&lt;a href="https://notestoself.dev/posts/boring-considered-beneficial/"&gt;Boring considered
beneficial&lt;/a&gt;&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;One of the main thoughts there was that approaches and technologies sometimes
considered boring or traditional have that perception for a reason: they&amp;rsquo;ve
become mature enough to generally work and solve problems in a straightforward
way. The community is used to them, so they&amp;rsquo;re not as exciting as more novel and
exotic ways of solving the same problems.&lt;/p&gt;</description></item><item><title>Scalar type declarations in PHP 7</title><link>https://notestoself.dev/posts/scalar-type-declarations-php7/</link><pubDate>Fri, 30 Sep 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/scalar-type-declarations-php7/</guid><description>&lt;p&gt;One of my favourite new features in PHP 7 is [scalar type
declarations](&lt;a href="http://php.net/manual/en/migration70.new-"&gt;http://php.net/manual/en/migration70.new-&lt;/a&gt;
features.php#migration70.new-features.scalar-type-declarations).&lt;/p&gt;
&lt;p&gt;Prior to PHP 7, the language&amp;rsquo;s type declaration abilities were somewhat
inconsistent. You could type hint classes, interfaces and arrays, but nothing
else (including &lt;em&gt;callable&lt;/em&gt; and &lt;em&gt;self&lt;/em&gt; in those). To require types for any scalar
values (strings, ints, bools and floats), you had to do your own type checking
and coercion.&lt;/p&gt;
&lt;p&gt;However, it is worth pointing out that it was and is possible to do deeper type
checking in PHP using value objects or interfaces. This is a nice technique and
has some added benefits over plain scalar type declarations. For example,
declaring &lt;em&gt;Weight&lt;/em&gt;, &lt;em&gt;Distance&lt;/em&gt; and &lt;em&gt;Length&lt;/em&gt; interfaces is more rigorous than
just declaring int or float types for those values.&lt;/p&gt;</description></item><item><title>Always lint</title><link>https://notestoself.dev/posts/always-lint/</link><pubDate>Tue, 13 Sep 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/always-lint/</guid><description>&lt;blockquote&gt;
&lt;p&gt;&amp;ldquo;lint (n): undesirable bits of fiber and fluff found in sheep&amp;rsquo;s wool&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;— &lt;a href="https://en.wikipedia.org/wiki/Lint_(software)"&gt;Wikipedia&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Linting is one of the first things I like to set up on a new project. Especially
when working with interpreted languages, it&amp;rsquo;s reassuring to develop with a
mechnical helper pointing out mistakes, problems and potential improvements.&lt;/p&gt;
&lt;p&gt;Linters exist for pretty much every language out there, and are a great addition
to the development process. Here&amp;rsquo;s why.&lt;/p&gt;</description></item><item><title>Trigger Warning</title><link>https://notestoself.dev/reading/trigger-warning/</link><pubDate>Mon, 05 Sep 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/trigger-warning/</guid><description/></item><item><title>The Handmaid's Tale</title><link>https://notestoself.dev/reading/the-handmaids-tale/</link><pubDate>Fri, 05 Aug 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-handmaids-tale/</guid><description/></item><item><title>Python unique random function result decorator</title><link>https://notestoself.dev/posts/python-unique-random-function-result-decorator/</link><pubDate>Sat, 23 Jul 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/python-unique-random-function-result-decorator/</guid><description>&lt;p&gt;I like messing around with random generators for productivity and creativity
systems. Usually this involves functions that return a random value on each
call. Even with quite a large set of possible values, duplicates occur more
often than you might expect.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a quick Python decorator that keeps calling the function it decorates
until it gets a value we haven&amp;rsquo;t seen so far during the current process:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;unique_results&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Callable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrapper&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;res&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;OverflowError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;Hit random value limit&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;wrapper&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then you can decorate your random result function like this:&lt;/p&gt;</description></item><item><title>Boring considered beneficial</title><link>https://notestoself.dev/posts/boring-considered-beneficial/</link><pubDate>Mon, 23 May 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/boring-considered-beneficial/</guid><description>&lt;p&gt;Being boring is a good thing in software design.&lt;/p&gt;
&lt;p&gt;There is no benefit to being impressive with a design, or to demonstrate one&amp;rsquo;s
knowledge in code. Doing that is likely to cause problems and reduce the quality
of the software.&lt;/p&gt;
&lt;p&gt;One aspect of the programming profession is that there is both an urge and
plenty of opportunity to try to show off. We are continually learning and
improving, as we should be, and this can lead us to actively seek out
opportunities to use the interesting knowledge we have acquired, instead of
putting it to use when a concrete need for it arises.&lt;/p&gt;</description></item><item><title>Simple pipes with PHP generators</title><link>https://notestoself.dev/posts/simple-pipes-php-generators/</link><pubDate>Tue, 17 May 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/simple-pipes-php-generators/</guid><description>&lt;p&gt;Pipes are a great structural pattern that can simplify the design of all kinds
of software. They&amp;rsquo;re a central part of the Unix philosophy, allowing users to
chain together a series of simple parts to achieve a more complex goal.
Similarly, gulpjs brings the benefit of a pipe-based design to asset generation
in the nodejs ecosystem.&lt;/p&gt;
&lt;p&gt;I haven&amp;rsquo;t seen pipes used so much in the PHP world, but they&amp;rsquo;ve been simple to
set up since
&lt;a href="https://secure.php.net/manual/en/language.generators.overview.php"&gt;generators&lt;/a&gt;
were added to the language in 5.4.&lt;/p&gt;</description></item><item><title>Never Let Me Go</title><link>https://notestoself.dev/reading/never-let-me-go/</link><pubDate>Thu, 05 May 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/never-let-me-go/</guid><description>From the Booker Prize-winning author of The Remains of the Day and When We Were Orphans, comes an unforgettable edge-of-your-seat mystery that is at once heartbreakingly tender and morally courageous about what it means to be human. Hailsham seems like a pleasant English boarding school, far from the influences of the city. Its students are well tended and supported, trained in art and literature, and become just the sort of people the world wants them to be. But, curiously, they are taught nothing of the outside world and are allowed little contact with it. Within the grounds of Hailsham, Kathy grows from schoolgirl to young woman, but it&amp;rsquo;s only when she and her friends Ruth and Tommy leave the safe grounds of the school (as they always knew they would) that they realize the full truth of what Hailsham is.</description></item><item><title>Using a real facade pattern in Laravel to encapsulate common behaviour</title><link>https://notestoself.dev/posts/real-facade-laravel-encapsulate-common-behaviour/</link><pubDate>Thu, 05 May 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/real-facade-laravel-encapsulate-common-behaviour/</guid><description>&lt;p&gt;Whilst working on my side project &lt;a href="https://www.poprobincards.co.uk/"&gt;Pop Robin Cards&lt;/a&gt;
(&lt;a href="https://github.com/hughgrigg/poprobincards"&gt;source&lt;/a&gt;), one of the PHP-MD rules I
have configured flagged an issue:&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;poprobincards/app/Http/Controllers/Staff/ProductController.php:53
The method __construct has 6 parameters. Consider to reduce parameter number
under 5.
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;PHP-MD is quite rightly pointing out that six parameters for a method is
excessive, even for a constructor. But what can be done? The six parameters are
all things that the &lt;code&gt;ProductController&lt;/code&gt; needs to do its job:&lt;/p&gt;</description></item><item><title>PHP's list() is asking for a class</title><link>https://notestoself.dev/posts/php-list-asking-for-class/</link><pubDate>Sun, 17 Apr 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/php-list-asking-for-class/</guid><description>&lt;p&gt;PHP has a weird mechanism for achieving multiple return values with the &lt;code&gt;list&lt;/code&gt;
function. It lets you do things like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="nx"&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;severalThings&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;thing&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;another thing&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;one more thing&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$bar&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$foobar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;severalThings&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This is quite commonly used in some codebases, but I think it&amp;rsquo;s bad style.&lt;/p&gt;
&lt;p&gt;Firstly, because PHP doesn&amp;rsquo;t offer a concrete way to let the user of a function
know exactly what it&amp;rsquo;s returning, it may not be clear what you&amp;rsquo;re getting from
that array. The function could even not return an array at all, in which case
trying to use &lt;code&gt;list&lt;/code&gt; on its return value will silently set your variables to
null.&lt;/p&gt;</description></item><item><title>Chinese strftime</title><link>https://notestoself.dev/posts/chinese-strftime/</link><pubDate>Tue, 05 Apr 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/chinese-strftime/</guid><description>&lt;p&gt;I&amp;rsquo;ve always used some variation of the original GNOME desktop: GNOME 2, Cinnamon
and currently MATE. All of those desktops feature a similar calendar applet that
lets you enter a strftime string to get the date formatted how you like it.&lt;/p&gt;
&lt;p&gt;To get a tiny bit more Chinese into my day, I use this format:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;%Y年%m月%e号 %A %p%l点%M分%S秒&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;(The main reason I&amp;rsquo;m writing this post is to keep the format easily accessible
(somewhere.)&lt;/p&gt;</description></item><item><title>Optimised image handling with AWS and Laravel</title><link>https://notestoself.dev/posts/optimised-image-handling-aws-laravel/</link><pubDate>Sat, 19 Mar 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/optimised-image-handling-aws-laravel/</guid><description>&lt;p&gt;A central feature in my side project [Pop Robin Cards](&lt;a href="https://github.com/hughgrigg"&gt;https://github.com/hughgrigg&lt;/a&gt;
/poprobincards) is the management of product images. In the first iteration, images
uploaded by the user were simply stored locally on disk and served by nginx.
This approach works fine, but has some problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It uses disk space on the EC2 instance hosting the site, where disk space is
at a premium.&lt;/li&gt;
&lt;li&gt;It makes it harder to load balance between multiple application server
instances.&lt;/li&gt;
&lt;li&gt;It uses application server resources to serve images.&lt;/li&gt;
&lt;li&gt;If the EC2 instance storing the images goes down then the images may be lost.&lt;/li&gt;
&lt;li&gt;There are potential security issues around storing user-uploaded files on
the application server.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;S3 provides an ideal solution to those issues. It&amp;rsquo;s cheap, durable, fast and
centralised. With that in mind, I implemented a simple image processing system
to optimise uploaded images and transfer them to S3.&lt;/p&gt;</description></item><item><title>Stranger in a Strange Land</title><link>https://notestoself.dev/reading/stranger-in-a-strange-land/</link><pubDate>Tue, 08 Mar 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/stranger-in-a-strange-land/</guid><description>Stranger in a Strange Land, winner of the 1962 Hugo Award, is the story of Valentine Michael Smith, born during, and the only survivor of, the first manned mission to Mars. Michael is raised by Martians, and he arrives on Earth as a true innocent: he has never seen a woman and has no knowledge of Earth&amp;rsquo;s cultures or religions. But he brings turmoil with him, as he is the legal heir to an enormous financial empire, not to mention de facto owner of the planet Mars. With the irascible popular author Jubal Harshaw to protect him, Michael explores human morality and the meanings of love. He founds his own church, preaching free love and disseminating the psychic talents taught him by the Martians. Ultimately, he confronts the fate reserved for all messiahs. The impact of Stranger in a Strange Land was considerable, leading many children of the 60&amp;rsquo;s to set up households based on Michael&amp;rsquo;s water-brother nests. Heinlein loved to pontificate through the mouths of his characters, so modern readers must be willing to overlook the occasional sour note (Nine times out of ten, if a girl gets raped, it&amp;rsquo;s partly her fault.). That aside, Stranger in a Strange Land is one of the master&amp;rsquo;s best entertainments, provocative as he always loved to be. Can you grok it? &amp;ndash;Brooks Peck</description></item><item><title>PyCharm Python script run error "ImportError: attempted relative import with no known parent package"</title><link>https://notestoself.dev/posts/pycharm-python-script-import-error-attempted-relative-import/</link><pubDate>Tue, 23 Feb 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/pycharm-python-script-import-error-attempted-relative-import/</guid><description>&lt;p&gt;When trying to run a Python script in PyCharm via a run configuration, you might
hit an error like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Traceback (most recent call last):
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; File &amp;#34;.../__main__.py&amp;#34;, line 3, in &amp;lt;module&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; from ...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ImportError: attempted relative import with no known parent package
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Process finished with exit code 1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;You might be able to resolve this by setting up the PyCharm run configuration as
a &amp;ldquo;Module name&amp;rdquo; rather than a &amp;ldquo;Script path&amp;rdquo;.&lt;/p&gt;</description></item><item><title>Pop Robin Cards</title><link>https://notestoself.dev/work/poprobincards/</link><pubDate>Sat, 23 Jan 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/poprobincards/</guid><description>&lt;p&gt;Pop Robin Cards is a side project that my wife and I work on in our spare time.
It&amp;rsquo;s a custom e-commerce platform selling
&lt;a href="https://www.poprobincards.co.uk/" title="3D Pop Up Cards | Pop Robin Cards London"&gt;3D pop-up cards&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The system allows management of a product catalogue, stock, images and so on, as
well as a sales and order tracking system.&lt;/p&gt;</description></item><item><title>Selecting the last grid row in CSS</title><link>https://notestoself.dev/posts/css-select-last-grid-row/</link><pubDate>Sat, 23 Jan 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/css-select-last-grid-row/</guid><description>&lt;p&gt;Recently I had the need to select the last row of elements in a grid using CSS.
The selector needed to be able to handle an arbitrary total number of elements
in the grid; in other words, in a grid with &lt;!-- raw HTML omitted --&gt;x&lt;!-- raw HTML omitted --&gt; columns, there could be 0
to &lt;!-- raw HTML omitted --&gt;x&lt;!-- raw HTML omitted --&gt; elements in the last row.&lt;/p&gt;</description></item><item><title>23 Things They Dont Tell You About Capitalism</title><link>https://notestoself.dev/reading/23-things-they-dont-tell-you-about-capitalism/</link><pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/23-things-they-dont-tell-you-about-capitalism/</guid><description/></item><item><title>59 Seconds</title><link>https://notestoself.dev/reading/59-seconds/</link><pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/59-seconds/</guid><description>A psychologist and best-selling author gives us a myth-busting response to the self-help movement, with tips and tricks to improve your life that come straight from the scientific community.Richard Wiseman has been troubled by the realization that the self-help industry often promotes exercises that destroy motivation, damage relationships, and reduce creativity: the opposite of everything it promises. Now, in 59 Seconds, he fights back, bringing together the diverse scientific advice that can help you change your life in under a minute, and guides you toward becoming more decisive, more imaginative, more engaged, and altogether more happy.From mood to memory, persuasion to procrastination, resilience to relationships, Wiseman outlines the research supporting the new science of �rapid change� and, with clarity and infectious enthusiasm, describes how these quirky, sometimes counterintuitive techniques can be effortlessly incorporated into your everyday life. Or, as he likes to say: �Think a little, change a lot.�</description></item><item><title>A Bitter Revolution</title><link>https://notestoself.dev/reading/a-bitter-revolution/</link><pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/a-bitter-revolution/</guid><description/></item><item><title>Budding Prospects</title><link>https://notestoself.dev/reading/budding-prospects/</link><pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/budding-prospects/</guid><description/></item><item><title>Fluent Forever</title><link>https://notestoself.dev/reading/fluent-forever/</link><pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/fluent-forever/</guid><description>Presents a series of techniques for acquiring a foreign language, including news ways of training the tongue for pronunciation, use of visual imagery for connecting sounds and spelling, and spaced-repetition methods for learning new vocabulary.</description></item><item><title>Fooled by Randomness</title><link>https://notestoself.dev/reading/fooled-by-randomness/</link><pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/fooled-by-randomness/</guid><description/></item><item><title>Haunted</title><link>https://notestoself.dev/reading/haunted/</link><pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/haunted/</guid><description/></item><item><title>Look to Windward</title><link>https://notestoself.dev/reading/look-to-windward/</link><pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/look-to-windward/</guid><description>The Twin Novae battle had been one of the last of the Idiran war, and one of the most horrific: desperate to avert their inevitable defeat, the Idirans had induced not one but two suns to explode, snuffing out worlds and biospheres teeming with sentient life. They were attacks of incredible proportion &amp;ndash; gigadeathcrimes. But the war ended, and life went on. Now, eight hundred years later, light from the first explosion is about to reach the Masaq&amp;rsquo; Orbital, home to the Culture&amp;rsquo;s most adventurous and decadent souls. There it will fall upon Masaq&amp;rsquo;s 50 billion inhabitants, gathered to commemorate the deaths of the innocent and to reflect, if only for a moment, on what some call the Culture&amp;rsquo;s own complicity in the terrible event. Also journeying to Masaq&amp;rsquo; is Major Quilan, an emissary from the war-ravaged world of Chel. In the aftermath of the conflict that split his world apart, most believe he has come to Masaq&amp;rsquo; to bring home Chel&amp;rsquo;s most brilliant star and self-exiled dissident, the honored Composer Ziller. Ziller claims he will do anything to avoid a meeting with Major Quilan, who he suspects has come to murder him. But the Major&amp;rsquo;s true assignment will have far greater consequences than the death of a mere political dissident, as part of a conspiracy more ambitious than even he can know &amp;ndash; a mission his superiors have buried so deeply in his mind that even he cannot remember it. Hailed by SFX magazine as an excellent hopping-on point if you&amp;rsquo;ve never read a Banks SF novel before, Look to Windward is an awe-inspiring immersion into the wildly original, vividly realized civilization that Banks calls the Culture.</description></item><item><title>Survivor</title><link>https://notestoself.dev/reading/survivor/</link><pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/survivor/</guid><description/></item><item><title>The Art of Thinking Clearly</title><link>https://notestoself.dev/reading/the-art-of-thinking-clearly/</link><pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-art-of-thinking-clearly/</guid><description/></item><item><title>The Brothers Karamazov</title><link>https://notestoself.dev/reading/the-brothers-karamazov/</link><pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-brothers-karamazov/</guid><description/></item><item><title>The Colour of Magic</title><link>https://notestoself.dev/reading/the-colour-of-magic/</link><pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-colour-of-magic/</guid><description/></item><item><title>The Left Hand of Darkness</title><link>https://notestoself.dev/reading/the-left-hand-of-darkness/</link><pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-left-hand-of-darkness/</guid><description/></item><item><title>The Light Fantastic</title><link>https://notestoself.dev/reading/the-light-fantastic/</link><pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-light-fantastic/</guid><description>Only one individual can save the world from a disastrous collision. Unfortunately, the hero happens to be the singularly inept wizard Rincewind, who was last seen falling off the edge of the world.</description></item><item><title>The Trial</title><link>https://notestoself.dev/reading/the-trial/</link><pubDate>Fri, 01 Jan 2016 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-trial/</guid><description>Kafka&amp;rsquo;s classic novel of the terror state, in which profound unease and sinister uncertainty simmer beneath Joseph K.&amp;rsquo;s surface world of desperate normality and security. When arrested, he is spun into a panic and slowly, strangely, a helpless sense of guilt emerges in him.</description></item><item><title>Island counting, max island area and max island perimeter in Python</title><link>https://notestoself.dev/posts/python-island-counting-max-area-perimeter-algorithms/</link><pubDate>Wed, 23 Dec 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/python-island-counting-max-area-perimeter-algorithms/</guid><description>&lt;p&gt;Here are three similar algorithms in Python for operations on &amp;ldquo;island problems&amp;rdquo;.
These are problems were the input is a 2D matrix of grid coordinates, where &lt;code&gt;0&lt;/code&gt;
represents sea and &lt;code&gt;1&lt;/code&gt; represents land.&lt;/p&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;It&amp;rsquo;s slightly hard to see, but there are 5 islands in this grid: one in each
corner, and a larger contiguous one in the shape of an L.&lt;/p&gt;</description></item><item><title>Keep controllers thin</title><link>https://notestoself.dev/posts/keep-controllers-thin/</link><pubDate>Mon, 23 Nov 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/keep-controllers-thin/</guid><description>&lt;p&gt;The world of professional web development is dominated by the &lt;a href="http://c2.com/cgi/wiki?ModelViewController"&gt;MVC
pattern&lt;/a&gt;. MVC is so common in
object oriented web development that it seems fundamental: &lt;em&gt;of course&lt;/em&gt; your
app has models, views and controllers (although other approaches could be
perfectly valid).&lt;/p&gt;
&lt;p&gt;One bit of advice that tends to get passed around for implementing with
the MVC pattern is &amp;ldquo;thin controllers and fat models&amp;rdquo;, or some variation of
that. I think this maxim misses the point.&lt;/p&gt;</description></item><item><title>Validating URLs with the DOM</title><link>https://notestoself.dev/posts/validating-urls-with-the-dom/</link><pubDate>Mon, 05 Oct 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/validating-urls-with-the-dom/</guid><description>&lt;p&gt;URL validation is a common task in Web development, and thankfully a lot of
cases can now be handled by the browser with the &lt;a href="http://www.w3.org/TR/html-markup/input.url.html"&gt;url input
type&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;url&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;gimme-a-link&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;That&amp;rsquo;s great and gives you a lot of functionality and good user experience on a
range of devices. However, a couple of issues remain once you&amp;rsquo;ve got your &lt;code&gt;url&lt;/code&gt;
type input in place:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The browser&amp;rsquo;s URL validation is fast and accurate, but can be too permissive.&lt;/li&gt;
&lt;li&gt;Accessing the result of the validation is currently [a mixed-bag](https://
developer.mozilla.org/en-US/docs/Web/API/ValidityState)
across browsers.&lt;/li&gt;
&lt;li&gt;You can&amp;rsquo;t do fine-grained validation according to specific circumstances.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This leads to implementing your own front-end URL validation either to
complement or replace that provided by the browser. If you are rolling your own,
here&amp;rsquo;s a request from me as a user: please keep the &lt;code&gt;url&lt;/code&gt; type and add a
[novalidate attribute](&lt;a href="http://www.w3.org/TR/html-"&gt;http://www.w3.org/TR/html-&lt;/a&gt;
markup/form.html#form.attrs.novalidate) if you want to disable the browser&amp;rsquo;s
validation &amp;ndash; this keeps the UX benefits which are particularly helpful on touch
devices.&lt;/p&gt;</description></item><item><title>Specification driven development</title><link>https://notestoself.dev/posts/specification-driven-development/</link><pubDate>Wed, 23 Sep 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/specification-driven-development/</guid><description>&lt;p&gt;There&amp;rsquo;s a great &lt;a href="https://github.com/grzesiek-galezowski/tdd-ebook"&gt;free ebook on test driven
development&lt;/a&gt; by Grzegorz
Gałęzowski. I read a lot of books related to programming, and this one stood out
as particularly worthwhile.&lt;/p&gt;
&lt;p&gt;Aside from a range of useful approaches and gems of understanding I got from the
book, the biggest gain for me was an appreciation that test driven development
isn&amp;rsquo;t &lt;em&gt;really&lt;/em&gt; about testing as such. It could instead be named &amp;ldquo;specification-
driven development&amp;rdquo;, which to me gives a better sense of what its main benefits
are (and is what TDD actually means).&lt;/p&gt;</description></item><item><title>Making random memorable passwords with bash</title><link>https://notestoself.dev/posts/random-memorable-passwords-bash/</link><pubDate>Thu, 17 Sep 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/random-memorable-passwords-bash/</guid><description>&lt;p&gt;Making secure passwords is quite a common task, and there are plenty of random
password generators around (I like the one in
&lt;a href="https://www.keepassx.org/"&gt;keepassx&lt;/a&gt;). Making memorable passwords can be a bit
trickier, though.&lt;/p&gt;
&lt;p&gt;A nice way to make random but memorable passwords is to use a sequence of words
and perhaps a short number (&lt;a href="https://xkcd.com/936/"&gt;obligatory link to xkcd&lt;/a&gt;).
That kind of password is easy for a human to remember and generally hard to
brute-force, so long as the pool of possible words is large enough.&lt;/p&gt;</description></item><item><title>Environment variables in the constructor are a code smell</title><link>https://notestoself.dev/posts/code-smell-env-vars-in-constructor/</link><pubDate>Sun, 06 Sep 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/code-smell-env-vars-in-constructor/</guid><description>&lt;p&gt;&lt;strong&gt;TLDR: don&amp;rsquo;t use env vars in class constructors.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Seeing environment variables in a class constructor is a code smell and an
anti-pattern.&lt;/p&gt;
&lt;p&gt;For example in TypeScript for Node.js, you might see something like this:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Anti-pattern:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;Foobar&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;niceInjectableDependency&lt;/span&gt;: &lt;span class="kt"&gt;IDependency&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;importantConfig&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="kr"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;niceInjectableDependency&lt;/span&gt;: &lt;span class="kt"&gt;IDependency&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;niceInjectableDependency&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;niceInjectableDependency&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;		&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;importantConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;IMPORTANT_CONFIG&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// code smell
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;	&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This might seem reasonable, as we&amp;rsquo;re still kind of able to inject the
&lt;code&gt;importantConfig&lt;/code&gt; into the class, just not via the constructor. Unfortunately
this injectability is nowhere near as good as the usual kind with function call
arguments.&lt;/p&gt;</description></item><item><title>Improving Unix-fu with Anki</title><link>https://notestoself.dev/posts/unix-fu-anki/</link><pubDate>Wed, 01 Jul 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/unix-fu-anki/</guid><description>&lt;p&gt;&lt;a href="http://ankisrs.net/"&gt;Anki&lt;/a&gt; has helped me maintain and improve my
&lt;a href="https://www.chineseboost.com/blog"&gt;Chinese&lt;/a&gt; for years, but I&amp;rsquo;ve always been
intrigued by using it to develop other kinds of knowledge. At university I used
it heavily for East Asian history, our modern Chinese literature course and
classical Chinese. I&amp;rsquo;ve now started using it to improve my Unix command-line
fluency, and the results have been quite dramatic.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not really possible to &lt;em&gt;learn&lt;/em&gt; programming with an SRS system like Anki,
but you can certainly use it to reinforce tokenized knowledge (like
[this](&lt;a href="https://developer.atlassian.com/blog/2015/06/golang-flashcards-and-"&gt;https://developer.atlassian.com/blog/2015/06/golang-flashcards-and-&lt;/a&gt;
spaced-repetition/)). Whilst programming ability doesn&amp;rsquo;t tokenize very well, a
lot of related computing skills certainly do.&lt;/p&gt;</description></item><item><title>Minimum required lecture rooms scheduling in Python</title><link>https://notestoself.dev/posts/minimum-required-lecture-rooms-scheduling-python/</link><pubDate>Tue, 23 Jun 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/minimum-required-lecture-rooms-scheduling-python/</guid><description>&lt;p&gt;Given a schedule of lectures represented as a list of tuples of start and end
times like &lt;code&gt;[(15, 16), (13, 15), (14, 15)]&lt;/code&gt;, return the minimum number of
lecture rooms required to run all of the lectures.&lt;/p&gt;
&lt;p&gt;We can solve this in $O(n\log_2n)$, due to the need to sort the lecture times.&lt;/p&gt;
&lt;p&gt;The overall steps are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Convert time tuples into &amp;ldquo;lecture event&amp;rdquo; tuples of time and type, i.e. start or end.&lt;/li&gt;
&lt;li&gt;Sort the lecture events by time and then type.&lt;/li&gt;
&lt;li&gt;Iterate lecture events, tracking current active lectures.&lt;/li&gt;
&lt;li&gt;Track the max active lectures.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A Python implementation looks like this:&lt;/p&gt;</description></item><item><title>Using Wget for recursive downloads</title><link>https://notestoself.dev/posts/wget-recursive-downloads/</link><pubDate>Sun, 31 May 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/wget-recursive-downloads/</guid><description>&lt;p&gt;I discovered the &lt;a href="http://vimcasts.org/"&gt;VimCasts&lt;/a&gt; website recently and have been
watching a few of their videos. Due to my unreliable internet connection, I
prefer to download whole video files and then watch them (this also allows
watching them on portable devices).&lt;/p&gt;
&lt;p&gt;VimCasts allow directory listing on their &lt;a href="http://media.vimcasts.org/videos/"&gt;storage
server&lt;/a&gt;, which I believe is permission to
bulk download their content. The only issue is that each episode is stored in
its own subdirectory and in two formats. I don&amp;rsquo;t want the folder structure and I
only want one of the formats.&lt;/p&gt;</description></item><item><title>That code is ugly for a reason</title><link>https://notestoself.dev/posts/ugly-code/</link><pubDate>Wed, 27 May 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/ugly-code/</guid><description>&lt;p&gt;Before I really got into development, I went through the entire archive of &lt;a href="http://www.joelonsoftware.com/"&gt;Joel
on Software&lt;/a&gt; reading every single article. I
like to think that it set me up with good foundational knowledge for
professional software development, but that&amp;rsquo;s pretty hard to prove.&lt;/p&gt;
&lt;p&gt;Recently I re-read the article &lt;a href="http://www.joelonsoftware.com/articles/fog0000000069.html"&gt;Things You Should Never Do, Part
I&lt;/a&gt;. With a lot more
development experience under my belt, the article spoke to me much more strongly
this time around.&lt;/p&gt;</description></item><item><title>Using Gulp asset versioning with Hugo data files</title><link>https://notestoself.dev/posts/gulp-asset-versioning-hugo-data-files/</link><pubDate>Sat, 23 May 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/gulp-asset-versioning-hugo-data-files/</guid><description>&lt;p&gt;I recently finished converting four of the websites I run from Wordpress to
&lt;a href="http://gohugo.io/"&gt;Hugo&lt;/a&gt; (my &lt;a href="https://eastasiastudent.net/"&gt;original blog&lt;/a&gt;, a
&lt;a href="http://www.chineseboost.com/blog/"&gt;Chinese learning blog&lt;/a&gt;, a &lt;a href="https://www.chineseboost.com/grammar/"&gt;Chinese grammar
site&lt;/a&gt; and this blog), and hosting them
with S3 and CloudFront.&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s been a great change to make and has improved everything from my workflow to
loading times and hosting costs. I&amp;rsquo;d recommend anyone running blogs and other
text-focused sites to switch away from Wordpress and use a static site generator
instead. Hugo would be my recommendation because of its efficiency and
flexibility, but there are many options.&lt;/p&gt;</description></item><item><title>Avoid naive string indexing in PHP</title><link>https://notestoself.dev/posts/avoid-naive-string-indexing-php/</link><pubDate>Thu, 23 Apr 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/avoid-naive-string-indexing-php/</guid><description>&lt;p&gt;PHP allows treating a string as an array, so that you can use indexing syntax to
get or set a single position in the string:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="nx"&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;hello&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// h
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;bar&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$foo&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;z&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$foo&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// baz
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This seems handy but you should never do it.&lt;/p&gt;
&lt;p&gt;The reason to avoid string indexing like this in PHP is that PHP strings are not
multibyte character strings, they&amp;rsquo;re just bytes.&lt;/p&gt;
&lt;p&gt;You won&amp;rsquo;t notice this with plain ASCII strings like the above, as each character
happens to be one byte anyway so they&amp;rsquo;re equivalent.&lt;/p&gt;</description></item><item><title>Typing arrow and quote characters in Ubuntu with AltGr</title><link>https://notestoself.dev/posts/ubuntu-altgr-type-arrow-quote-characters/</link><pubDate>Mon, 23 Feb 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/ubuntu-altgr-type-arrow-quote-characters/</guid><description>&lt;p&gt;A quick Ubuntu Linux tip: you can use the &lt;code&gt;AltGr&lt;/code&gt; key to easily type various
arrow and quote characters.&lt;/p&gt;
&lt;p&gt;The ones you might commonly want to use are:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-txt" data-lang="txt"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;AltGr + y ← Left arrow	
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;AltGr + u ↓ Down arrow
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;AltGr + i → Right arrow
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;AltGr + Shift + u ↑ Up arrow
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;AltGr + z « Left double angle quote
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;AltGr + x » Right double angle quote
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;AltGr + v “ Left double quotation mark
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;AltGr + b ” Right double quotation mark
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Topological course prerequisites in Python</title><link>https://notestoself.dev/posts/python-topological-course-prerequisites/</link><pubDate>Fri, 23 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/python-topological-course-prerequisites/</guid><description>&lt;p&gt;We want to figure out the sequence we can take university courses in, and some
of the courses may have prerequisite courses. In other words, we want a
topological sort of the courses.&lt;/p&gt;
&lt;p&gt;For this example, we can just ignore any courses that are impossible to take
because they depend on non-existent courses, or have a circular dependency.&lt;/p&gt;
&lt;p&gt;The input data is like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;C3&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;C2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;C1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;C2&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;C1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;C1&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And the desired output is like this:&lt;/p&gt;</description></item><item><title>Alexs Adventures in Numberland</title><link>https://notestoself.dev/reading/alexs-adventures-in-numberland/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/alexs-adventures-in-numberland/</guid><description/></item><item><title>Bash Cookbook</title><link>https://notestoself.dev/reading/bash-cookbook/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/bash-cookbook/</guid><description/></item><item><title>Blink: The Power of Thinking Without Thinking</title><link>https://notestoself.dev/reading/blink/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/blink/</guid><description>Blink is about the first two seconds of looking&amp;ndash;the decisive glance that knows in an instant. Gladwell, the best-selling author of The Tipping Point, campaigns for snap judgments and mind reading with a gift for translating research into splendid storytelling. Building his case with scenes from a marriage, heart attack triage, speed dating, choking on the golf course, selling cars, and military maneuvers, he persuades readers to think small and focus on the meaning of thin slices of behavior. The key is to rely on our adaptive unconscious&amp;ndash;a 24/7 mental valet&amp;ndash;that provides us with instant and sophisticated information to warn of danger, read a stranger, or react to a new idea. Gladwell includes caveats about leaping to conclusions: marketers can manipulate our first impressions, high arousal moments make us mind blind, focusing on the wrong cue leaves us vulnerable to the Warren Harding Effect (i.e., voting for a handsome but hapless president). In a provocative chapter that exposes the dark side of blink, he illuminates the failure of rapid cognition in the tragic stakeout and murder of Amadou Diallo in the Bronx. He underlines studies about autism, facial reading and cardio uptick to urge training that enhances high-stakes decision-making. In this brilliant, cage-rattling book, one can only wish for a thicker slice of Gladwell&amp;rsquo;s ideas about what Blink Camp might look like. &amp;ndash;Barbara Mackoff</description></item><item><title>Consider Phlebas</title><link>https://notestoself.dev/reading/consider-phlebas/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/consider-phlebas/</guid><description/></item><item><title>David and Goliath</title><link>https://notestoself.dev/reading/david-and-goliath/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/david-and-goliath/</guid><description>In the past decade, Malcolm Gladwell has written three books that have radically changed how we understand our world and ourselves: The Tipping Point, Blink, and Outliers. Regarded by many as the most gifted and influential author and journalist in America today, Gladwell&amp;rsquo;s rare ability to connect with audiences of such varied interests has ensured that each title become a phenomenal bestseller with more than ten million copies in print combined. Now, Gladwell&amp;rsquo;s landmark investigations into the world around us are collected together for the first time. Beautifully repackaged and redesigned, including for the first time illustrations throughout each book, MALCOLM GLADWELL: COMPLETE is a perfect treasury of prose and provocation for Gladwell fans old and new.</description></item><item><title>Dead Air</title><link>https://notestoself.dev/reading/dead-air/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/dead-air/</guid><description/></item><item><title>Distrust That Particular Flavour</title><link>https://notestoself.dev/reading/distrust-that-particular-flavour/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/distrust-that-particular-flavour/</guid><description/></item><item><title>Effective Programming: More Than Writing Code</title><link>https://notestoself.dev/reading/effective-programming-more-than-writing-code/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/effective-programming-more-than-writing-code/</guid><description>ABOUT THE BOOK Jeff Atwood began the Coding Horror blog in 2004, and is convinced that it changed his life. He needed a way to keep track of software development over time - whatever he was thinking about or working on. He researched subjects he found interesting, then documented his research with a public blog post, which he could easily find and refer to later. Over time, increasing numbers of blog visitors found the posts helpful, relevant and interesting. Now, approximately 100,000 readers visit the blog per day and nearly as many comment and interact on the site. Effective Programming: More Than Writing Code is your one-stop shop for all things programming. Jeff writes with humor and understanding, allowing for both seasoned programmers and newbies to appreciate the depth of his research. From such posts as The Programmer&amp;rsquo;s Bill of Rights and Why Cant Programmers&amp;hellip; Program? to Working With the Chaos Monkey, this book introduces the importance of writing responsible code, the logistics involved, and how people should view it more as a lifestyle than a career. TABLE OF CONTENTS - Introduction - The Art of Getting Shit Done - Principles of Good Programming - Hiring Programmers the Right Way - Getting Your Team to Work Together - The Batcave: Effective Workspaces for Programmers - Designing With the User in Mind - Security Basics: Protecting Your Users&amp;rsquo; Data - Testing Your Code, So it Doesn&amp;rsquo;t Suck More Than it Has To - Building, Managing and Benefiting from a Community - Marketing Weasels and How Not to Be One - Keeping Your Priorities Straight EXCERPT FROM THE BOOK As a software developer, you are your own worst enemy. The sooner you realize that, the better off you&amp;rsquo;ll be.I know you have the best of intentions. We all do. We&amp;rsquo;re software developers; we love writing code. It&amp;rsquo;s what we do. We never met a problem we couldn&amp;rsquo;t solve with some duct tape, a jury-rigged coat hanger and a pinch of code. But Wil Shipley argues that we should rein in our natural tendencies to write lots of code: The fundamental nature of coding is that our task, as programmers, is to recognize that every decision we make is a trade-off. To be a master programmer is to understand the nature of these trade-offs, and be conscious of them in everything we write.In coding, you have many dimensions in which you can rate code: Brevity of codeFeaturefulnessSpeed of executionTime spent codingRobustnessFlexibility Now, remember, these dimensions are all in opposition to one another. You can spend three days writing a routine which is really beautiful and fast, so you&amp;rsquo;ve gotten two of your dimensions up, but you&amp;rsquo;ve spent three days, so the time spent coding dimension is way down.So, when is this worth it? How do we make these decisions? The answer turns out to be very sane, very simple, and also the one nobody, ever, listens to: Start with brevity. Increase the other dimensions as required by testing. I couldn&amp;rsquo;t agree more. I&amp;rsquo;ve given similar advice when I exhorted developers to Code Smaller. And I&amp;rsquo;m not talking about a reductio ad absurdum contest where we use up all the clever tricks in our books to make the code fit into less physical space. I&amp;rsquo;m talking about practical, sensible strategies to reduce the volume of code an individual programmer has to read to understand how a program works. Here&amp;rsquo;s a trivial little example of what I&amp;rsquo;m talking about: if (s == String.Empty)if (s == ) It seems obvious to me that the latter case is&amp;hellip; &amp;hellip;buy the book to read more!</description></item><item><title>Everything I Know</title><link>https://notestoself.dev/reading/everything-i-know/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/everything-i-know/</guid><description>The authors provide the new teacher with guidance and advice that is full of encouragement, humor, and practical ideas, all based on real first-year experiences.</description></item><item><title>Excession</title><link>https://notestoself.dev/reading/excession/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/excession/</guid><description/></item><item><title>Head First Design Patterns</title><link>https://notestoself.dev/reading/head-first-design-patterns/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/head-first-design-patterns/</guid><description/></item><item><title>High Performance MySql</title><link>https://notestoself.dev/reading/high-performance-mysql/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/high-performance-mysql/</guid><description/></item><item><title>How to Stop Sucking and Be Awesome Instead</title><link>https://notestoself.dev/reading/how-to-stop-sucking-and-be-awesome-instead/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/how-to-stop-sucking-and-be-awesome-instead/</guid><description/></item><item><title>Inviting Disaster</title><link>https://notestoself.dev/reading/inviting-disaster/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/inviting-disaster/</guid><description/></item><item><title>Learning PHP Design Patterns</title><link>https://notestoself.dev/reading/learning-php-design-patterns/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/learning-php-design-patterns/</guid><description>With the design patterns in this book, you�ll be able to build PHP applications much more efficiently. Learning PHP Design Patterns shows you how to use patterns through simple examples, and then demonstrates many of them in full-fledged working applications. You�ll learn patterns that help you connect PHP and MySQL, as well as several pattern categories that encapsulate object-oriented programming practices and concepts, such as polymorphism.These patterns allow you to adopt a more sophisticated programming style, focusing on language improvements introduced in PHP 5. You�ll also learn patterns that help you avoid common programming problems.</description></item><item><title>Matter</title><link>https://notestoself.dev/reading/matter/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/matter/</guid><description>In a world renowned even within a galaxy full of wonders, a crime within a war. For one man it means a desperate flight, and a search for the one - maybe two - people who could clear his name. For his brother it means a life lived under constant threat of treachery and murder. And for their sister, even without knowing the full truth, it means returning to a place she&amp;rsquo;d thought abandoned forever.Only the sister is not what she once was; Djan Seriy Anaplian has changed almost beyond recognition to become an agent of the Culture&amp;rsquo;s Special Circumstances section, charged with high-level interference in civilizations throughout the greater galaxy.Concealing her new identity - and her particular set of abilities - might be a dangerous strategy, however. In the world to which Anaplian returns, nothing is quite as it seems; and determining the appropriate level of interference in someone else&amp;rsquo;s war is never a simple matter.MATTER is a novel of dazzling wit and serious purpose. An extraordinary feat of storytelling and breathtaking invention on a grand scale, it is a tour de force from a writer who has turned science fiction on its head.</description></item><item><title>Never Let Me Go</title><link>https://notestoself.dev/reading/never-let-me-go/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/never-let-me-go/</guid><description/></item><item><title>Predictably Irrational</title><link>https://notestoself.dev/reading/predictably-irrational/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/predictably-irrational/</guid><description/></item><item><title>The Algebraist</title><link>https://notestoself.dev/reading/the-algebraist/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-algebraist/</guid><description/></item><item><title>The Emperor Far Away</title><link>https://notestoself.dev/reading/the-emperor-far-away/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-emperor-far-away/</guid><description>For more than two thousand years, as capital of the powerful Nan Yue kingdom to its present position as the largest city in southern China, Canton has been one of Asia&amp;rsquo;s most important commercial, political, and cultural centres. Arab merchants first arrived in the Tang dynasty, trading spices, amber, and ivory for silk, ceramics, and precious stones. During the eighteen century, for the next hundred years, it was the only port in China where the emperor allowed merchants from the West to do business. With trade came the interchange of ideas. Missionaries, educators, political leaders, and modernizers, Chinese and non-Chinese alike, all tapped Canton&amp;rsquo;s energy in their efforts to direct the course of China&amp;rsquo;s cultural and political development.</description></item><item><title>The Establishment, and How They Get Away With It</title><link>https://notestoself.dev/reading/the-establishment/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-establishment/</guid><description>Why they cheat&amp;hellip; Presidents, religious leaders, CEOs, school teachers, moms, dads, young, old&amp;hellip;they all do it and it&amp;rsquo;s all too common even in what many of us would consider &amp;lsquo;great&amp;rsquo; relationships-but why? One recent survey showed that nearly 70% of respondents in self-described monogamous relationships have confessed they&amp;rsquo;ve either thought about cheating, have cheated, or are currently cheating&amp;hellip;and their partner has no idea. In Why They Cheat and How They Get Away With It, J.L. Ford explains why almost every relationship is at risk and exposes that aspect of human nature that compels otherwise responsible men and women to act on the impulse to cheat-regardless of the personal or professional consequences.</description></item><item><title>The Hydrogen Sonata</title><link>https://notestoself.dev/reading/the-hydrogen-sonata/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-hydrogen-sonata/</guid><description/></item><item><title>The Player of Games</title><link>https://notestoself.dev/reading/the-player-of-games/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-player-of-games/</guid><description/></item><item><title>The Pragmatic Programmer</title><link>https://notestoself.dev/reading/the-pragmatic-programmer/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-pragmatic-programmer/</guid><description>Straight from the programming frenches, The Pragmatic Programmer cuts through the increasing specialization and technicalities of modern software development to examine the core process - taking a requirement and producing working, maintainable code that delights its users. It covers topics ranging from personal responsibility and career development to architectural techniques for keeping your code flexible and easy to adapt and reuse.&amp;ndash;BOOK JACKET.</description></item><item><title>The State of the Art</title><link>https://notestoself.dev/reading/the-state-of-the-art/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-state-of-the-art/</guid><description/></item><item><title>The Undercover Economist</title><link>https://notestoself.dev/reading/the-undercover-economist/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-undercover-economist/</guid><description/></item><item><title>The Way to Go</title><link>https://notestoself.dev/reading/the-way-to-go/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-way-to-go/</guid><description>This book provides the reader with a comprehensive overview of the new open source programming language Go (in its first stable and maintained release Go 1) from Google. The language is devised with Java / C#-like syntax so as to feel familiar to the bulk of programmers today, but Go code is much cleaner and simpler to read, thus increasing the productivity of developers. You will see how Go: simplifies programming with slices, maps, structs and interfaces incorporates functional programming makes error-handling easy and secure simplifies concurrent and parallel programming with goroutines and channels And you will learn how to: make use of Go&amp;rsquo;s excellent standard library program Go the idiomatic way using patterns and best practices in over 225 working examples and 135 exercises This book focuses on the aspects that the reader needs to take part in the coming software revolution using Go.</description></item><item><title>Thinking Statistically</title><link>https://notestoself.dev/reading/thinking-statistically/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/thinking-statistically/</guid><description>The paperback printing of Thinking Statistically includes all the material from the Kindle original plus innovative new segments giving graphical representations of statistics concepts. Thinking Statistically is the book that shows you how to think like a statistician, without worrying about formal statistical techniques. Along the way we learn how selection bias can explain why your boss doesn�t know he sucks (even when everyone else does); how to use Bayes� Theorem to decide if your partner is cheating on you; and why Mark Zuckerberg should never be used as an example for anything. See the world in a whole new light, and make better decisions and judgements without ever going near a t-test. Think. Think Statistically.</description></item><item><title>三分鍾小說</title><link>https://notestoself.dev/reading/%E4%B8%89%E5%88%86%E9%8D%BE%E5%B0%8F%E8%AA%AA/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/%E4%B8%89%E5%88%86%E9%8D%BE%E5%B0%8F%E8%AA%AA/</guid><description/></item><item><title>遊戲玩家</title><link>https://notestoself.dev/reading/%E9%81%8A%E6%88%B2%E7%8E%A9%E5%AE%B6/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/%E9%81%8A%E6%88%B2%E7%8E%A9%E5%AE%B6/</guid><description/></item><item><title>駱駝祥子</title><link>https://notestoself.dev/reading/%E9%A7%B1%E9%A7%9D%E7%A5%A5%E5%AD%90/</link><pubDate>Thu, 01 Jan 2015 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/%E9%A7%B1%E9%A7%9D%E7%A5%A5%E5%AD%90/</guid><description/></item><item><title>Straightforward recursive number-to-words algorithm in TypeScript</title><link>https://notestoself.dev/posts/straightforward-recursive-number-to-words-function-typescript/</link><pubDate>Fri, 26 Dec 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/straightforward-recursive-number-to-words-function-typescript/</guid><description>&lt;p&gt;This is a recursive number-to-words algorithm in TypeScript. It tries to be
straightforward and just uses if statements to make the logic a bit easier to
follow.&lt;/p&gt;
&lt;p&gt;The key ideas are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Let the recursion do the work.&lt;/li&gt;
&lt;li&gt;Handle &amp;ldquo;and&amp;rdquo; carefully.&lt;/li&gt;
&lt;li&gt;Don&amp;rsquo;t move up in factors of ten; move up in word groups.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;numberToWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt;: &lt;span class="kt"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sb"&gt;`minus &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;numberToWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;DIRECT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;DIRECT&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;units&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;units&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;DIRECT&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tens&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;numberToWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;units&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sub_hundreds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hundreds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;sub_hundreds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;numberToWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hundreds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; hundred`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sub_hundreds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="sb"&gt;` and &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;numberToWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sub_hundreds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sub_thous&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;thousands&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;sub_thous&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;numberToWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;thousands&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; thousand`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sub_thous&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sub_thous&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; and&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="sb"&gt;` &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;numberToWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sub_thous&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sub_mills&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;millions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;num&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;sub_mills&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;numberToWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;millions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt; million`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sub_mills&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sub_mills&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; and&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;words&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="sb"&gt;` &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;numberToWords&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sub_mills&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;words&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;I can&amp;#39;t count that high sorry&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Jest tests:&lt;/p&gt;</description></item><item><title>TypeScript recursive power function</title><link>https://notestoself.dev/posts/typescript-recursive-array-product-function-algorithm/</link><pubDate>Fri, 26 Dec 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/typescript-recursive-array-product-function-algorithm/</guid><description>&lt;p&gt;This is a &lt;code&gt;productOfArray(numbers)&lt;/code&gt; function in TypeScript that uses recursion.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;productOfArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numbers&lt;/span&gt;: &lt;span class="kt"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;productOfArray&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Jest tests:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productOfArray&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;multiplies array elements together&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productOfArray&lt;/span&gt;&lt;span class="p"&gt;([])).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productOfArray&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;])).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productOfArray&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;])).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productOfArray&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;])).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;125&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productOfArray&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;])).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;productOfArray&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;])).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;362880&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>TypeScript recursive factorial function</title><link>https://notestoself.dev/posts/typescript-recursive-factorial-function-algorithm/</link><pubDate>Thu, 25 Dec 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/typescript-recursive-factorial-function-algorithm/</guid><description>&lt;p&gt;This is a &lt;code&gt;factorial(factor)&lt;/code&gt; function in TypeScript that uses recursion.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;factor&lt;/span&gt;: &lt;span class="kt"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;factor&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;factor&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;factor&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Jest tests:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;recursive factorial&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;720&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;factorial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5040&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>TypeScript recursive power function</title><link>https://notestoself.dev/posts/typescript-recursive-power-function-algorithm/</link><pubDate>Wed, 24 Dec 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/typescript-recursive-power-function-algorithm/</guid><description>&lt;p&gt;This is a &lt;code&gt;power(base, exponent)&lt;/code&gt; function in TypeScript that uses recursion
instead of the &lt;code&gt;Math.pow()&lt;/code&gt; function.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;: &lt;span class="kt"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;exponent&lt;/span&gt;: &lt;span class="kt"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;exponent&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;exponent&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Jest tests:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-typescript" data-lang="typescript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;power&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;2^x&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;3^x&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;power&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;81&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>A simple string obscure function in PHP</title><link>https://notestoself.dev/posts/php-string-obscure-function/</link><pubDate>Tue, 23 Dec 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/php-string-obscure-function/</guid><description>&lt;p&gt;It&amp;rsquo;s quite common to need to obscure sensitive information when displaying it to
a client. For example, it&amp;rsquo;s safer to display a user&amp;rsquo;s email address as
&lt;code&gt;h***.g****@g****.c**&lt;/code&gt;. If someone views the page, the user&amp;rsquo;s personal
information isn&amp;rsquo;t fully revealed, but the user can still confirm that it&amp;rsquo;s
correct.&lt;/p&gt;
&lt;p&gt;This post is just a note of a helper function to do this in PHP.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-php" data-lang="php"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="nx"&gt;php&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt;/**
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; * @param string|string[] $plain
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; * @param int $revealStart
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; * @param int $revealEnd
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; * @param string $obscuration
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; *
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; * @return string|string[]
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; */&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;obscure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$plain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$revealStart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$revealEnd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$obscuration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;*&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;is_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$plain&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;array_map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$plainPart&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$revealStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$revealEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$obscuration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;obscure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$plainPart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$revealStart&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$revealEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$obscuration&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$plain&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$plain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;$plain&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mb_substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$plain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$revealStart&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;str_repeat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$obscuration&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;mb_strlen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$plain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$revealStart&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nv"&gt;$revealEnd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;.&lt;/span&gt; &lt;span class="nx"&gt;mb_substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$plain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nv"&gt;$revealEnd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$revealEnd&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that the function can take an array of strings and call itself recursively
on them if necessary.&lt;/p&gt;</description></item><item><title>The observer pattern and side effects</title><link>https://notestoself.dev/posts/observer-pattern-side-effects/</link><pubDate>Wed, 05 Nov 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/observer-pattern-side-effects/</guid><description>&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Observer_pattern"&gt;observer pattern&lt;/a&gt; is
extremely common in a lot of software, particularly in web apps. Most web
frameworks, ORMs and other boilerplate systems have a built in structure for
events and observers, making it easy to listen to various built-in events and to
fire customised events of your own.&lt;/p&gt;
&lt;p&gt;The observer pattern is one of the classic design patterns &amp;ndash; it&amp;rsquo;s number 7 in
the Gang of Four &amp;ndash; and is one of the patterns that I deal with most commonly
day to day. It&amp;rsquo;s an attractive design pattern as it allows a lot of flexibility
in a system. For example, using observers makes it easier to adhere to the
&lt;a href="https://en.wikipedia.org/wiki/Open/closed_principle"&gt;open-closed principle&lt;/a&gt; by
hooking into events without needing to meddle with the existing system. It often
feels &amp;ldquo;clean&amp;rdquo; to listen to an event instead of making changes to existing code.&lt;/p&gt;</description></item><item><title>Initial commit</title><link>https://notestoself.dev/posts/initial-commit/</link><pubDate>Thu, 23 Oct 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/initial-commit/</guid><description>&lt;p&gt;This is the first post on my new blog. I&amp;rsquo;ll be writing about various unrelated
things that happen to interest me at the time. This blog used to be on
WordPress, then Jekyll hosted on Github pages, and this iteration uses
&lt;a href="http://gohugo.io/"&gt;Hugo&lt;/a&gt; and AWS for hosting. I believe I&amp;rsquo;ll stick with Hugo
and AWS for some time as I&amp;rsquo;ve never enjoyed a CMS and hosting setup as much as
this.&lt;/p&gt;
&lt;p&gt;Hugo is a pleasure to use due to its speed and flexibility. It works wonderfully
for a minimalist blog like this, as you might expect, but I&amp;rsquo;ve also been able to
build &lt;a href="https://www.chineseboost.com/grammar/"&gt;something with a slightly more complex
structure&lt;/a&gt;, and can see that Hugo could
easily handle far more complexity than that. It&amp;rsquo;s also been great reading the
&lt;a href="https://github.com/spf13/hugo/"&gt;Hugo source code&lt;/a&gt;, as recently I&amp;rsquo;ve become more
and more interested in Go.&lt;/p&gt;</description></item><item><title>Learning vocabulary on the command line</title><link>https://notestoself.dev/posts/learning-vocabulary-on-the-commandline/</link><pubDate>Thu, 23 Oct 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/learning-vocabulary-on-the-commandline/</guid><description>&lt;p&gt;I like the idea of adding little bits of learning to my day that don&amp;rsquo;t take
much effort once I&amp;rsquo;ve set them up. They&amp;rsquo;re not the most effective way to learn,
but they certainly don&amp;rsquo;t hurt and every now and then you do get something
worthwhile out of them.&lt;/p&gt;
&lt;p&gt;One of the things I&amp;rsquo;ve set up recently is displaying random Chinese vocabulary
items at the start of every command line session in Zsh.&lt;/p&gt;</description></item><item><title>LoveCrafts</title><link>https://notestoself.dev/work/lovecrafts/</link><pubDate>Mon, 22 Sep 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/lovecrafts/</guid><description>&lt;p&gt;In 2014-2015, I worked on the ecommerce platform, which is based on a heavily
customised Magento EE installation with integrations to other business systems.&lt;/p&gt;
&lt;p&gt;I then worked on the Social API team through to 2018, building a central APIs
and microservices for the LoveCrafts social network using code-generation
techniques.&lt;/p&gt;</description></item><item><title>China Checkup</title><link>https://notestoself.dev/work/china-checkup/</link><pubDate>Thu, 23 Jan 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/china-checkup/</guid><description>&lt;p&gt;I was the sole developer on ChinaCheckup.com from 2013 to 2014, handling all
front-end and back-end development, as well as managing the server.&lt;/p&gt;
&lt;p&gt;China Checkup sells complex business analysis services to clients outside of
China. The site is a custom ecommerce solution based on the Laravel framework.
It automates much of the workflow in creating, modifying and selling these
services to clients.&lt;/p&gt;
&lt;p&gt;Some time after I left the company, China Checkup migrated to the Shopify
platform.&lt;/p&gt;</description></item><item><title>Longest distinct substring algorithm in JavaScript</title><link>https://notestoself.dev/posts/longest-distinct-substring-algorithm-javascript/</link><pubDate>Thu, 09 Jan 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/longest-distinct-substring-algorithm-javascript/</guid><description>&lt;p&gt;Given a string, find the longest substring of distinct characters.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;findLongestDistinctSubstring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastPos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;maxLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="kr"&gt;char&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;char&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;lastPos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// Can&amp;#39;t go backwards, so use max() to ensure
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lastPos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;char&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;lastPos&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;char&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;maxLength&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxLength&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;maxLength&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This solution has $O(n)$ time complexity because we iterate the input string
once, and $O(n)$ space complexity because we build up a mapping of input
characters to positions.&lt;/p&gt;</description></item><item><title>Min subarray length algorithm in JavaScript</title><link>https://notestoself.dev/posts/min-sub-array-length-algorithm-javascript/</link><pubDate>Wed, 08 Jan 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/min-sub-array-length-algorithm-javascript/</guid><description>&lt;p&gt;Given an array of positive intergers and a positive integer target, return the
length of the shortest possible contiguos sub-array with a sum greater than or
equal to the target.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;minSubArrayLen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;currSum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;minLen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;Infinity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;currSum&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;targ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;currSum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;right&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;minLen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minLen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;right&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;currSum&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;left&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;minLen&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;Infinity&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;minLen&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This solution has $O(n)$ time complexity because we iterate the main array once,
and $O(1)$ space complexity because we only store a fixed set of variables while
moving through the main array.&lt;/p&gt;</description></item><item><title>Max sub array sum algorithm in JavaScript</title><link>https://notestoself.dev/posts/max-sub-array-sum-algorithm-javascript/</link><pubDate>Tue, 07 Jan 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/max-sub-array-sum-algorithm-javascript/</guid><description>&lt;p&gt;Given an array of numbers and an integer &amp;ldquo;width&amp;rdquo;, find the max sum of a
contiguous sub-array with that width in positions.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;maxSubArraySum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;subArraySum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;maxSubArraySum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;subArraySum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;subArraySum&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;subArraySum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;numbers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;maxSubArraySum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;maxSubArraySum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;subArraySum&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;subArraySum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This algorithm has $O(n)$ time complexity and $O(1)$ space complexity.&lt;/p&gt;</description></item><item><title>String is subsequence algorithm in JavaScript</title><link>https://notestoself.dev/posts/string-is-subsequence-algorithm-javascript/</link><pubDate>Mon, 06 Jan 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/string-is-subsequence-algorithm-javascript/</guid><description>&lt;p&gt;Given two strings &lt;code&gt;needle&lt;/code&gt; and &lt;code&gt;haystack&lt;/code&gt;, check if &lt;code&gt;needle&lt;/code&gt; is
a &lt;strong&gt;subsequence&lt;/strong&gt; (not necessarily a substring) of &lt;code&gt;haystack&lt;/code&gt; at any point.
That means that the letters in &lt;code&gt;needle&lt;/code&gt; must appear in &lt;code&gt;haystack&lt;/code&gt; in the same
order, but not necessarily contiguously.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;isSubsequence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;needle&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;haystack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;needle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;haystack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;needle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;haystack&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;needle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;ni&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hi&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;haystack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;haystack&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;hi&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;needle&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;ni&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;ni&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ni&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;needle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;hi&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This algorithm has $O(m+n)$ time complexity and $O(1)$ space complexity.&lt;/p&gt;</description></item><item><title>Check for target average pair in sorted array algorithm in JavaScript</title><link>https://notestoself.dev/posts/sorted-array-average-pair-algorithm-javascript/</link><pubDate>Sun, 05 Jan 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/sorted-array-average-pair-algorithm-javascript/</guid><description>&lt;p&gt;Given a &lt;strong&gt;sorted&lt;/strong&gt; array of numbers and a target average value, return whether
or not it is possible to achieve the target average value with a pair of numbers
from the array.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;canMakeAveragePair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;avg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;avg&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;avg&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;avg&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;h&lt;/span&gt;&lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This algorithm is $O(n)$ time and $O(1)$ space.&lt;/p&gt;</description></item><item><title>Duplicate detection algorithm in JavaScript</title><link>https://notestoself.dev/posts/numbers-digits-same-frequency-algorithm-javascript/</link><pubDate>Sat, 04 Jan 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/numbers-digits-same-frequency-algorithm-javascript/</guid><description>&lt;p&gt;Here&amp;rsquo;s a super simple duplicate detection algorithm in JavaScript:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;hasDuplicate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;items&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This algorithm is $O(n)$ time and $O(n)$ space.&lt;/p&gt;</description></item><item><title>Count unique values algorithm in JavaScript</title><link>https://notestoself.dev/posts/count-unique-values-algorithm-javascript/</link><pubDate>Fri, 03 Jan 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/count-unique-values-algorithm-javascript/</guid><description>&lt;p&gt;Given a &lt;strong&gt;sorted&lt;/strong&gt; array of values like &lt;code&gt;[1, 1, 2, 3, 3, 4, 5, 5, 5]&lt;/code&gt;, count the
number of unique values in $O(n)$ time and $O(1)$ space.&lt;/p&gt;
&lt;p&gt;If there wasn&amp;rsquo;t a requirement to do this in $O(1)$ space, the most
straightforward way to do it would be to use a &lt;code&gt;Set()&lt;/code&gt;. We could iterate the
input array adding each element to the set, then return &lt;code&gt;set.size&lt;/code&gt;. This would
be $O(n)$ time but $O(n)$ space, so it wouldn&amp;rsquo;t meet the space requirement.&lt;/p&gt;</description></item><item><title>Valid anagram algorithm in JavaScript</title><link>https://notestoself.dev/posts/valid-anagram-algorithm-javascript/</link><pubDate>Thu, 02 Jan 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/valid-anagram-algorithm-javascript/</guid><description>&lt;p&gt;Given two strings such as &lt;code&gt;&amp;quot;listen&amp;quot;&lt;/code&gt; and &lt;code&gt;&amp;quot;silent&amp;quot;&lt;/code&gt;, return whether or not they
are valid anagrams of each other. That is, the second word uses the same letters
as the first but potentially in a different order.&lt;/p&gt;
&lt;p&gt;One approach is to make a frequency count of the first string, and then iterate
the second string &amp;ldquo;checking off&amp;rdquo; each letter from the frequency count of the
first. If we hit a missing letter or reduce the corresponding frequency count to
zero, we know it&amp;rsquo;s not a valid anagram. If we&amp;rsquo;re able to check off every letter,
then it is a valid anagram.&lt;/p&gt;</description></item><item><title>Number digits same frequency algorithm in JavaScript</title><link>https://notestoself.dev/posts/numbers-digits-same-frequency-algorithm-javascript/</link><pubDate>Wed, 01 Jan 2014 00:00:00 +0000</pubDate><guid>https://notestoself.dev/posts/numbers-digits-same-frequency-algorithm-javascript/</guid><description>&lt;p&gt;Given two numbers such as &lt;code&gt;1987456&lt;/code&gt; and &lt;code&gt;5796184&lt;/code&gt;, check if the two numbers have
the same frequency of digits. That is, they have the same digits the same number
of times. Another way of putting this would be to check if the digits of the two
numbers are anagrams of each other or not.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-javascript" data-lang="javascript"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;sameFrequency&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;freq_a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;freq_b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;freq_a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;freq_a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;freq_b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;freq_b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;digit&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;freq_a&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;freq_a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;digit&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;freq_b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;digit&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This algorithm is $O(n)$ where $n$ is the number of digits, as it iterates twice
to a maximum of $n$.&lt;/p&gt;</description></item><item><title>Personal blog</title><link>https://notestoself.dev/work/hugh-grigg-com/</link><pubDate>Wed, 23 Oct 2013 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/hugh-grigg-com/</guid><description>&lt;p&gt;The site you&amp;rsquo;re on now :-)&lt;/p&gt;</description></item><item><title>Chinese Boost</title><link>https://notestoself.dev/work/chinese-boost/</link><pubDate>Thu, 23 May 2013 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/chinese-boost/</guid><description>&lt;p&gt;As East Asia Student got increasingly eclectic and unfocused, I decided to
branch off the &amp;lsquo;how to learn Chinese&amp;rsquo; content to this new site. Like my other
text-based sites, Chinese Boost is built using Hugo and hosted with AWS.&lt;/p&gt;
&lt;p&gt;I get quite a lot of nice feedback from people saying how useful they find
Chinese Boost.&lt;/p&gt;</description></item><item><title>East Asia Student</title><link>https://notestoself.dev/work/east-asia-student/</link><pubDate>Wed, 05 May 2010 00:00:00 +0000</pubDate><guid>https://notestoself.dev/work/east-asia-student/</guid><description>&lt;p&gt;East Asia Student was more active whilst I was at university, but I still add
new content from time to time. The site is now built using Hugo and hosted with
AWS.&lt;/p&gt;</description></item><item><title>1440 Daily Digest</title><link>https://notestoself.dev/reading/1440-daily-digest/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/1440-daily-digest/</guid><description/></item><item><title>Aeon</title><link>https://notestoself.dev/reading/aeon/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/aeon/</guid><description/></item><item><title>Book Freak</title><link>https://notestoself.dev/reading/book-freak/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/book-freak/</guid><description/></item><item><title>Delancey Place</title><link>https://notestoself.dev/reading/delancey-place/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/delancey-place/</guid><description/></item><item><title>Freek.dev</title><link>https://notestoself.dev/reading/freek.dev/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/freek.dev/</guid><description/></item><item><title>Hacker News Digest</title><link>https://notestoself.dev/reading/hacker-news-digest/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/hacker-news-digest/</guid><description/></item><item><title>Hacker Newsletter</title><link>https://notestoself.dev/reading/hacker-newsletter/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/hacker-newsletter/</guid><description/></item><item><title>Hire Me</title><link>https://notestoself.dev/hire-me/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/hire-me/</guid><description/></item><item><title>Inkstone</title><link>https://notestoself.dev/reading/inkstone/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/inkstone/</guid><description/></item><item><title>James Clear 3-2-1</title><link>https://notestoself.dev/reading/james-clear-3-2-1/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/james-clear-3-2-1/</guid><description/></item><item><title>Laravel News</title><link>https://notestoself.dev/reading/laravel-news/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/laravel-news/</guid><description/></item><item><title>Marginal Revolution</title><link>https://notestoself.dev/reading/marginal-revolution/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/marginal-revolution/</guid><description/></item><item><title>Monevator</title><link>https://notestoself.dev/reading/monevator/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/monevator/</guid><description/></item><item><title>Morning Brew</title><link>https://notestoself.dev/reading/morning-brew/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/morning-brew/</guid><description/></item><item><title>Mr Money Mustache</title><link>https://notestoself.dev/reading/mr-money-mustache/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/mr-money-mustache/</guid><description/></item><item><title>Nautilus</title><link>https://notestoself.dev/reading/nautilus/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/nautilus/</guid><description/></item><item><title>News Refinery</title><link>https://notestoself.dev/reading/news-refinery/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/news-refinery/</guid><description/></item><item><title>NextDraft</title><link>https://notestoself.dev/reading/next-draft/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/next-draft/</guid><description/></item><item><title>Ribbonfarm</title><link>https://notestoself.dev/reading/ribbonfarm/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/ribbonfarm/</guid><description/></item><item><title>Sinocism</title><link>https://notestoself.dev/reading/sinocism/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/sinocism/</guid><description/></item><item><title>Slate Star Codex</title><link>https://notestoself.dev/reading/slate-star-codex/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/slate-star-codex/</guid><description/></item><item><title>The Economist</title><link>https://notestoself.dev/reading/the-economist/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-economist/</guid><description/></item><item><title>The Week</title><link>https://notestoself.dev/reading/the-week/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/the-week/</guid><description/></item><item><title>Tim Harford</title><link>https://notestoself.dev/reading/tim-harford/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/tim-harford/</guid><description/></item><item><title>Typescript Weekly</title><link>https://notestoself.dev/reading/typescript-weekly/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/typescript-weekly/</guid><description/></item><item><title>Unintended Consequences</title><link>https://notestoself.dev/reading/unintended-consequences/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://notestoself.dev/reading/unintended-consequences/</guid><description/></item></channel></rss>