Peter’s blog ✴ Week 179 ✴ 22 August 2022

THE WEEKLY CHALLENGE
Ordinal sparkles

The Perl Camel

Task 1

Ordinal number spelling

You are given a positive number, $n. Write a script to spell the ordinal number.

Examples

11 => eleventh
62 => sixty-second
99 => ninety-ninth

Analysis

My solution is based on dividing the digits into 3s, eg 234, and converting them to words, eg two hundred and thirty-four and then adding 'thousands' or 'millions' as appropriate.

I then suffix 'th', and deal with the exceptions, eg 'ninth', not 'nineth'.

I special case zero and a billion, weed out invalid entries, and that's it - anything from 0 to a billion, which should be enough for most purposes.

Amended 08.06.2026

I am sorry that the 'Try it' feature is currently working very slowly or not at all owing to some issue with my web hosting provider.

Try it 

Try running the script with any input:



example: 12345

Script


#!/usr/bin/perl

# Blog: http://ccgi.campbellsmiths.force9.co.uk/challenge

use v5.26;    # The Weekly Challenge - 2022-08-22
use utf8;     # Week 179 - task 1 - Ordinal number spelling
use warnings; # Peter Campbell Smith
binmode STDOUT, ':utf8';

ordinal_number_spelling($_) for (0 .. 3, 8 .. 11, 14, 20, 44, 100, 321, 4321, 654321, 987654321, 1000000000, 3.14159);

sub ordinal_number_spelling {   

    my ($number, $digits, $words, $j, $piece);
    
    $number = shift;
    $words = 'invalid' if ($number > 10**9 or $number !~ m|^\d{0,9}$|);
    $words = 'zeroth' if $number == 0;
    $words = 'one billionth' if $number == 10**9;
    
    # make cardinal number
    unless (defined $words) {
        $words = '';
        $digits = sprintf('%09d', $number);
        for ($j = 0; $j <= 6; $j += 3) {
            $piece = three_digits(substr($digits, $j, 3));
            if ($piece) {
                $words .= $piece ;
                $words .= $j == 0 ? ' million ' : ($j == 3 ? ' thousand ' : '');
            }
        }
        $words =~ s|^\s+||;
        
        # convert to ordinal
        $words =~ s|\s*$|th|;
        $words =~ s|oneth$|first|;
        $words =~ s|twoth$|second|;
        $words =~ s|threeth$|third|;
        $words =~ s|fiveth$|fifth|;
        $words =~ s|eightth$|eighth|;
        $words =~ s|nineth$|ninth|;
        $words =~ s|tyth$|tieth|;
        $words =~ s|   | and |;
        $words =~ s|  | |;
    }
    
    say qq[\nInput:  \$n = $number];
    say qq[Output: $words];
}
    
sub three_digits {

    my (@digits, @tens, @teens, $hundreds, $words, $number, $tens, $units, $hyphen);
    
    # converts 3-digit number to words
    @digits = (' ', qw[one two three four five six seven eight nine]);
    @teens = qw[ten eleven twelve thirteen fourteen fifteen sixteen seventeen eighteen nineteen];
    @tens = (' ', ' ', qw[twenty thirty forty fifty sixty seventy eighty ninety]);

    # hundreds
    $number = shift;
    $hundreds = int($number / 100);
    $words = $hundreds ? $digits[$hundreds] . ' hundred' : '';
    $number -= $hundreds * 100;
    
    # tens and units    
    if ($number) {
        $words = $words . ' and' if $hundreds != 0;     
        $tens = int($number / 10);
        $units = $number - 10 * $tens;
        if ($tens != 1) {
            $hyphen = ($units and $tens) ? '-' : '';
            $words = $words . ' ' . $tens[$tens] . $hyphen . $digits[$units];
        } else {
            $words = $words . ' ' . $teens[$units];
        }
    }
    return $words;
}

46 lines of code
Completed after the closing date and not submitted to GitHub

Output from script


Input:  $n = 0
Output: zeroth

Input:  $n = 1
Output: first

Input:  $n = 2
Output: second

Input:  $n = 3
Output: third

Input:  $n = 8
Output: eighth

Input:  $n = 9
Output: ninth

Input:  $n = 10
Output: tenth

Input:  $n = 11
Output: eleventh

Input:  $n = 14
Output: fourteenth

Input:  $n = 20
Output: twentieth

Input:  $n = 44
Output: forty-fourth

Input:  $n = 100
Output: one hundredth

Input:  $n = 321
Output: three hundred and twenty-first

Input:  $n = 4321
Output: four thousand three hundred and twenty-first

Input:  $n = 654321
Output: six hundred and fifty-four thousand three hundred
        and twenty-first

Input:  $n = 987654321
Output: nine hundred and eighty-seven million six hundred
        and fifty-four thousand three hundred and 
        twenty-first

Input:  $n = 1000000000
Output: one billionth

Input:  $n = 3.14159
Output: invalid

 

Any content of this website which has been created by Peter Campbell Smith is in the public domain