Converting Celsius to Fahrenheit with Go

July 17, 2024
0 comments Go

This is a continuation of Converting Celsius to Fahrenheit with Python, and TypeScript, but in Go:


package main

import (
    "fmt"
    "math"
)

func c2f(c int) float64 {
    return float64(c)*9/5 + 32
}

func isMirror(a int, b int) bool {
    return reverseString(massage(a)) == massage(b)
}

func massage(n int) string {
    switch {
    case n < 10:
        return fmt.Sprintf("0%d", n)
    case n >= 100:
        return massage(n - 100)
    default:
        return fmt.Sprintf("%d", n)
    }
}

func reverseString(s string) string {
    runes := []rune(s)
    for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
        runes[i], runes[j] = runes[j], runes[i]
    }
    return string(runes)
}

func printConversion(c int, f int) {
    fmt.Println(fmt.Sprintf("%d°C ~= %d°F", c, f))
}

func main() {
    for c := 4; c < 100; c += 12 {
        var f = c2f(c)
        if isMirror(c, int(math.Ceil(f))) {
            printConversion(c, int(math.Ceil(f)))
        } else if isMirror(c, int(math.Floor(f))) {
            printConversion(c, int(math.Floor(f)))
        } else {
            break
        }
    }
}

Run it like this:


go run conversion.go

or build and run:


go build -o conversion-go conversion.go
./conversion-go

and the output becomes:

4°C ~= 40°F
16°C ~= 61°F
28°C ~= 82°F
40°C ~= 104°F
52°C ~= 125°F

Converting Celsius to Fahrenheit with TypeScript

July 16, 2024
0 comments Bun, JavaScript

This is a continuation of Converting Celsius to Fahrenheit with Python, but in TypeScript:


function c2f(c: number): number {
  return (c * 9) / 5 + 32;
}

function isMirror(a: number, b: number) {
  function massage(n: number) {
    if (n < 10) return `0${n}`;
    else if (n >= 100) return massage(n - 100);
    return `${n}`;
  }
  return reverseString(massage(a)) === massage(b);
}

function reverseString(str: string) {
  return str.split("").reverse().join("");
}

function printConversion(c: number, f: number) {
  console.log(`${c}°C ~= ${f}°F`);
}

for (let c = 4; c < 100; c += 12) {
  const f = c2f(c);
  if (isMirror(c, Math.ceil(f))) {
    printConversion(c, Math.ceil(f));
  } else if (isMirror(c, Math.floor(f))) {
    printConversion(c, Math.floor(f));
  } else {
    break;
  }
}

And when you run it:


❯ bun run conversion.ts
4°C ~= 40°F
16°C ~= 61°F
28°C ~= 82°F
40°C ~= 104°F
52°C ~= 125°F

Converting Celsius to Fahrenheit with Python

July 12, 2024
0 comments Python

Here's a useful mnemonic for remembering how to convert Celsius to Fahrenhait(*):

  • Start at 4°C
  • Add +12 each time
  • Flip the C in mirror, with some additional fudging

For example, 4°C is 04°C. Mirror image of "04" is "40". So 4°C equals 40°F.
And when there's a 1 in front, as in 125°F, look at that as 100 + 25°F. Mirror of 25°F is 52°C. So 52°C equals 125°F.

In Python it can be tested like this:


import math


def c2f(c):
    return c * 9 / 5 + 32


def is_mirror(a, b):
    def massage(n):
        if n < 10:
            return f"0{n}"
        elif n >= 100:
            return massage(n - 100)
        else:
            return str(n)

    return massage(a)[::-1] == massage(b)


def print_conv(c, f):
    print(f"{c}°C ~= {f}°F")


for i in range(4, 100, 12):
    f = c2f(i)
    if is_mirror(i, math.ceil(f)):
        print_conv(i, math.ceil(f))
    elif is_mirror(i, math.floor(f)):
        print_conv(i, math.floor(f))
    else:
        break

When you run that you get:

4°C ~= 40°F
16°C ~= 61°F
28°C ~= 82°F
40°C ~= 104°F
52°C ~= 125°F

(*) If you can't remember F = C × 9/5 + 32 or, perhaps, remember it but can't compute the arithmetic easily.

In TypeScript, how to combine known and unknown keys to an object

July 3, 2024
0 comments JavaScript

More than happy to be informed of a better solution here! But this came up in a real-work situation and I "stumbled" on the solution by more or less guessing.

In plain JavaScript, you have an object which you know you set certain keys on. But because this object is (ab)used for a templating engine, we also put keys/values on it that are not known in advance. In our use case, these keys and booleans came from parsing a .yml file which. It looks something like this:


// Code simplified for the sake of the example

const context = {
  currentVersion: "3.12", 
  currentLanguage: "en",
  activeDate: someDateObject,
  // ... other things that are values of type number, bool, Date, and string
  // ...
}
if (someCondition()) {
  context.hasSomething = true
}

for (const [featureFlag, truth] of Object.entries(parseYamlFile('features.yml')) {
  context[featureFlag] = truth
}

const rendered = render(template: { context })

I don't like this design where you "combine" an object with known keys with a spread of unknown keys coming from an external source. But here we are and we have to convert this to TypeScript, the clock's ticking!

In comes TypeScript

Intuitively, from skimming the simplified pseudo-code above you might try this:


type Context = {
  currentVersion: string
  currentLanguage: string
  activeDate: Date
  [featureFlag: string]: boolean
}

TS error

TypeScript Playground demo here

Except, it won't work:

Property 'currentVersion' of type 'string' is not assignable to 'string' index type 'boolean'.
Property 'currentLanguage' of type 'string' is not assignable to 'string' index type 'boolean'.
Property 'activeDate' of type 'Date' is not assignable to 'string' index type 'boolean'.

Make sense, right? We're saying the type should have this, that, and that, but also saying that it can be anything. So it's a conflict.

How I solved it

I'll be honest, I'm not sure this is very intuitive, either. But it works:


type FeatureFlags = {
  [featureFlag: string]: boolean
}
type Context = FeatureFlags & {
  currentVersion: string
  currentLanguage: string
  activeDate: Date
}

TypeScript Playground demo

It does imply that the inheritance, using the & is more than just semantic sugar. It means something.

Rate my golf swing (June 2024)

July 1, 2024
0 comments Golf

Never done this before but I thought it could be fun.

Things to keep in mind, I'm 45 years old and have only played for 2-3 years. My handicap is 8. I have a coach who has given me 5+ things to keep in mind, and I usually think only of 1-2 when I swing out on the course, and maybe 2-3 when I'm practicing on the mat.

What I'm currently working on is to not move the right hip too much to my right, but to instead load the power into the right thigh. I'm also trying to not bend my left (glove) hand but have a straight line from the underarm to the knuckles. The other thing I'm trying really hard to do is to move the club far out to my right in the first 20-30° with extended arms, rather than lifting the club up by my hands.

I'm super-far from an expert and I see lots of flaws compared to the pros. There's plenty of stuff still on my plate with the things I know I need to work on. Truth be told, at the moment, the thing I'm working on the most isn't swing mechanics but all mental; take it easy, and don't try to smash it.

Simple object lookup in TypeScript

June 14, 2024
2 comments JavaScript

Ever got this error:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ foo: string; bar: string; }'. No index signature with a parameter of type 'string' was found on type '{ foo: string; bar: string; }'.(7053)

Yeah, me too. What used to be so simple in JavaScript suddenly feels hard in TypeScript.

In JavaScript,


const greetings = {
  good: "Excellent",
  bad: "Sorry to hear",
}
const answer = prompt("How are you?")
if (typeof answer === "string") {
  alert(greetings[answer] || "OK")
}

To see it in action, I put it into a CodePen.

Now, port that to TypeScript,


const greetings = {
  good: "Excellent",
  bad: "Sorry to hear",
}
const answer = prompt("How are you?")
if (typeof answer === "string") {
  alert(greetings[answer] || "OK")
}

Same. Except it doesn't work.
You can view it here on the TypeScript playground

This is the error you get about greetings[answer]:

TypeScript playground with error

Full error:

Element implicitly has an 'any' type because the expression of type 'string' can't be used to index type '{ good: string; bad: string; }'. No index signature with a parameter of type 'string' was found on type '{ good: string; bad: string; }'.(7053)

The simplest way of saying is that that object greetings, does not have any keys that are type string. Instead, the object has keys that are exactly good and bad.

I'll be honest, I don't understand the exact details of why it works like this. What I do know is that I want the red squiggly lines to go away and for tsc to be happy.
But what makes sense, from TypeScript's point of view is that, at runtime the greetings object can change to be something else. E.g. greetings.bad = 123 and now greetings['bad'] would suddenly be a number. A wild west!

This works:


const greetings: Record<string, string> = {
  good: "Excellent",
  bad: "Sorry to hear",
}
const answer = prompt("How are you?")
if (typeof answer === "string") {
  alert(greetings[answer] || "OK")
}

All it does is that it says that the greetings object is always a strings-to-string object.

See it in the TypeScript playground here

This does not work:


const greetings = {
  good: "Excellent",
  bad: "Sorry to hear",
}
const answer = prompt("How are you?")
if (typeof answer === 'string') {
  alert(greetings[answer as keyof greetings] || "OK")  // DOES NOT WORK
}

To be able to use as keyof greetings you need to do that on a type, not on the object. E.g.
This works, but feels more clumsy:


type Greetings = {
  good: string
  bad: string
}
const greetings: Greetings = {
  good: "Excellent",
  bad: "Sorry to hear",
}
const answer = prompt("How are you?")
if (typeof answer === 'string') {
  alert(greetings[answer as keyof Greetings] || "OK")
}

In conclusion

TypeScript is awesome because it forces you to be more aware of what you're doing. Just because something happen(ed) to work in JavaScript, when you first type it, doesn't mean it will work later.

Note, I still don't know (please enlighten me), what's the best practice between...


const greetings: Record<string, string> = {

...versus...


const greetings: {[key:string]: string} = {

The latter had the advantage that you can give it a name, e.g. "key".

UPDATE (July 1, 2024)

Incorporating Gregor's utility function from the comment below yields this:


function isKeyOfObject<T extends object>(
  key: string | number | symbol,
  obj: T,
): key is keyof T {
  return key in obj;
}

const stuff = {
  foo: "Foo",
  bar: "Bar"
}
const v = prompt("What are you?")
if (typeof v === 'string') {
  console.log("Hello " + (isKeyOfObject(v, stuff) ? stuff[v] : "stranger"))
}

TypeScript Playground demo