Camel
Peter
Peter Campbell Smith

Echo and wordy numbers

Weekly challenge 362 — 23 February 2026

Week 362: 23 Feb 2026

Task 2

Task — Spellbound sorting

You are given an array of integers. Write a script to return them in alphabetical order, in any language of your choosing.

Examples


Example 1
Input: (6, 7, 8, 9 ,10)
Output: (8, 9, 7, 6, 10)
eight, nine, seven, six, ten

Example 2
Input: (-3, 0, 1000, 99)
Output: (-3, 99, 1000, 0)
minus three, ninety-nine, one thousand, zero

Example 3
Input: (1, 2, 3, 4, 5)
Output: (5, 2, 4, 3, 1) for French language
cinq, deux, quatre, trois, un
Output: (5, 4, 1, 3, 2) for English language
five, four, one, three, two

Example 4
Input: (0, -1, -2, -3, -4)
Output: (-4, -1, -3, -2, 0)
minus four, minus one, minus three, minus two, zero

Example 5
Input: (100, 101, 102)
Output: (100, 101, 102)
one hundred, one hundred and one, one hundred and two

Analysis

I submitted this challenge without thinking too hard about how easy it would be. The answer seems to be that it can be pretty easy or quite tricky, depending on your approach.

The tricky part is converting a number to its English language representation. The easy way to do that is to use a CPAN module, of which there are several candidates, and I chose one which provides a very concise interface:

use Lingua::EN::Numbers::Easy;
$words = $N{$number};

Its only slight drawback is that it returns, for example, -3 as negative 3. Also, it sometimes enters a comma where one isn't normally used.

That's my 'Output 1' solution.

The harder way to do it is avoid modules and code it all in Perl, which has to cope with a number of idiosyncracies in the English language and in the way numbers are expressed in English. In fact not all variants of English agree on this, so I have used the wording generally used in a formal context in the UK. That's my 'Output 2' solution.

Converting to words is not the end of the solution, however. In both cases I created an array where the elements are

$words . ' ' . $number

for example, twelve 12. That sorts in the correct order, and it's only then necessary to extract the trailing numbers (and minus sign) to create the desired output.

Try it 

Try running the script with any input:



example: -6, 7, 0, 8, 9, 1000

Script


#!/usr/bin/perl

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

use v5.26;    # The Weekly Challenge - 2026-02-23
use utf8;     # Week 362 - task 2 - Spellbound sorting
use warnings; # Peter Campbell Smith
binmode STDOUT, ':utf8';
use Encode;
use Lingua::EN::Numbers::Easy;

spellbound_sorting(6, 7, 8, 9, 10);
spellbound_sorting(-3, 0, 1000, 99);
spellbound_sorting(1, -2, 3, -4, 5);
spellbound_sorting(100, 1000, 1000000, 1000000000);
spellbound_sorting(0, 5, 0, 5);
spellbound_sorting(123, 10**12, -999);
spellbound_sorting(9, 99, 999, 9999);
spellbound_sorting(100123006);

sub spellbound_sorting {
    
    my (@input, @output, @words, $w, $method, $explain);
    
    # initialise
    @input = @_;
    
    # try two methods
    for $method (1 .. 2) {
        @output = ();
        $explain= '';
    
        # make array of eg 'twenty-one¦21'
        if ($method == 1) {
            $words[$_] = number_to_words1($input[$_]) . qq[ $input[$_]] for 0 .. $#input;
        } else {
            $words[$_] = number_to_words2($input[$_]) . qq[ $input[$_]] for 0 .. $#input;
        }           
        
        # sort it into alphabetical order
        @words = sort { $a cmp $b } @words;
        
        # extract numbers
        for $w (@words) {
            $w =~ m|(.*?) (-?\d+)|;
            push(@output, $2);
            $explain .= qq[$1; ];
        }
        
        # report
        say qq[\nInput:    (] . join(', ', @input) . ')' unless $method == 2;
        say qq[Output $method: (] . join(', ', @output) . ')';
        say qq[   ] . substr($explain, 0, -2);
    }
}

sub number_to_words1 {
    
    # using Lingua::EN::Numbers::Easy
    my $words = $N{$_[0]};
    $words =~ s|negative|minus|;
    return $words;
}

sub number_to_words2 {  

    # using Perl
    my ($number, $digits, $words, $j, $piece, $minus, @chunks, $three);
    @chunks = ('billion', 'million', 'thousand', '');
    
    # check for -ve number
    $number = shift;
    $minus = $number < 0;
    $number = -$number if $minus;
    
    # check for special cases
    return 'invalid' if ($number =~ m|[^\d]| or $number > 10**12);
    return 'zero' if $number == 0;
    return 'one trillion' if $number == 10**12;
    
    # make number into words 3 digits at a time
    $words = '';
    $digits = sprintf('%012d', $number);
    while ($digits =~ m|(\d\d\d)|g) {
        if ($1) {   # ie not 000
            $three = three_digits($1);
            $words .= ' and ' 
                if ($words and $chunks[0] eq '' and $1 =~ m|^0| and $three ne '');
            $words .= $three;
            
            # add 'million', 'thousand' etc
            $words .= ' ' . $chunks[0] . ' ' if $three;
            shift @chunks;
        }
    }
    
    # tidy it up
    $words =~ m|^\s*(.*?)\s*$|;
    $words = $1;
    $words =~ s|\s+| |g;
 
    return ($minus ? qq[minus $words] : $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];
        }
    }
    
    # tidy up spaces
    $words =~ m|^\s*(.*?)\s*$|;
    $words = $1;
    $words =~ s|\s+| |g;
    return $words;
}

Output


Input:    (6, 7, 8, 9, 10)
Output 1: (8, 9, 7, 6, 10)
   eight; nine; seven; six; ten
Output 2: (8, 9, 7, 6, 10)
   eight; nine; seven; six; ten

Input:    (-3, 0, 1000, 99)
Output 1: (-3, 99, 1000, 0)
   minus three; ninety-nine; one thousand; zero
Output 2: (-3, 99, 1000, 0)
   minus three; ninety-nine; one thousand; zero

Input:    (1, -2, 3, -4, 5)
Output 1: (5, -4, -2, 1, 3)
   five; minus four; minus two; one; three
Output 2: (5, -4, -2, 1, 3)
   five; minus four; minus two; one; three

Input:    (100, 1000, 1000000, 1000000000)
Output 1: (1000000000, 100, 1000000, 1000)
   one billion; one hundred; one million; one thousand
Output 2: (1000000000, 100, 1000000, 1000)
   one billion; one hundred; one million; one thousand

Input:    (0, 5, 0, 5)
Output 1: (5, 5, 0, 0)
   five; five; zero; zero
Output 2: (5, 5, 0, 0)
   five; five; zero; zero

Input:    (123, 1000000000000, -999)
Output 1: (-999, 123, 1000000000000)
   minus nine hundred and ninety-nine; one hundred and 
   twenty-three; one trillion
Output 2: (-999, 123, 1000000000000)
   minus nine hundred and ninety-nine; one hundred and 
   twenty-three; one trillion

Input:    (9, 99, 999, 9999)
Output 1: (9, 999, 9999, 99)
   nine; nine hundred and ninety-nine; nine thousand nine
   hundred and ninety-nine; ninety-nine
Output 2: (9, 999, 9999, 99)
   nine; nine hundred and ninety-nine; nine thousand nine
   hundred and ninety-nine; ninety-nine

Input:    (100123006)
Output 1: (100123006)
   one hundred million, one hundred and twenty-three 
   thousand and six
Output 2: (100123006)
   one hundred million one hundred and twenty-three 
   thousand and six

 

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