Ordinals in JavaScript
If you search for a way to format numbers into their ordinal representation (e.g. 1 becomes 1st, 2 becomes 2nd), you might come across hand-minified algorithms that inspire, but there's a widely available API that you almost certainly should be using instead.
The recent Intl.PluralRules
API makes it painless to define rules to format
numbers into human-readable strings. Unfortunately, it doesn’t make it painless
finding the rules in the first place, but I’ve had a decades of practice to get
us up and running.
The code below includes some type annotations, so it’s not really JavaScript anymore, but the lines are so blurred between Microsoft’s JavaScript and the version we used with jQuery in the early naughties that I beg the reader’s forgiveness.
const pr = new Intl.PluralRules("en-GB", { type: "ordinal" });
const suffixes = new Map([
["one", "st"],
["two", "nd"],
["few", "rd"],
["other", "th"],
]);
export function formatOrdinal(n: number): string {
const rule = pr.select(n);
const suffix = suffixes.get(rule);
return `${n}${suffix}`;
}
I also wrote some clumsy tests with vitest
because I want to make sure things
work without getting deep into the vitest
API (which seems a little anemic).
import { describe, expect, test } from "vitest";
import { formatOrdinal } from "@/lib/number";
describe("formatOrdinal", () => {
[
{ number: 0, string: "0th" },
{ number: 1, string: "1st" },
{ number: 2, string: "2nd" },
{ number: 3, string: "3rd" },
{ number: 4, string: "4th" },
{ number: 10, string: "10th" },
{ number: 11, string: "11th" },
{ number: 12, string: "12th" },
{ number: 13, string: "13th" },
{ number: 14, string: "14th" },
{ number: 100, string: "100th" },
{ number: 101, string: "101st" },
{ number: 102, string: "102nd" },
{ number: 103, string: "103rd" },
{ number: 104, string: "104th" },
].forEach(({ number, string }) => {
test(`with ${number}`, () => {
expect(formatOrdinal(number)).toBe(string);
});
});
});