The Weekly Challenge - 362

TASK #2: Spellbound Sorting
You are given an array of integers.

Write a script to return them in alphabetical order, in any language of your choosing. Default language is English.


Sorting by Something Else (An Easy Introduction on Schwartzian Transform)
Sometimes we don't want to sort values directly - we want to sort them by something derived from them.
In this task, we sort numbers by their spelled-out form in a given language (using Lingua::Any::Numbers).

For example (German): 3 2 21 1 becomes: drei zwei einundzwanzig eins

Now we sort those words alphabetically. That order gives us the numbers in this sequence: 3 1 21 2

If we compute the word form inside the sort comparison, Perl may call that function many times.
So the same number might be converted to its word form again and again. That can be inefficient.

Instead, we can:
1. Convert each number to its word once
2. Sort using those words
3. Remove the temporary words afterward

This technique is called the Schwartzian Transform. It simply means:
1. Decorate - attach the computed sort key
2. Sort - sort by that key
3. Undecorate - remove the temporary key

What Happens Internally
If we call:

spellbound_sorting(\@numbers, 'DE');
('DE' -> German language)

then each number is temporarily turned into:

[ word_form , original_number ]

Example:
["drei", 3]
["zwei", 2]
["einundzwanzig", 21]
["eins", 1]

The list is sorted by the word (index = 0), and then we extract the original number (index = 1).
The word is only temporary - it exists purely to control sorting.
My multi-language solution with Schwarzian Transform using only Lingua::Any::Numbers.

#!/usr/bin/perl
use strict;
use warnings;
use Lingua::Any::Numbers qw(num2str);

sub spellbound_sorting {

    my ($numbers, $lang) = @_;
    my @result;
    
# ---------------------------------------------------------------    
# The newbie long version:
# ---------------------------------------------------------------    
    # Step 1: Decorate
    # Attach the word form to each number
    my @decorated;
    for my $n (@$numbers) {
        my $word = num2str($n, $lang);
        push @decorated, [ $word, $n ];
    }

    # Step 2: Sort by the word (element [0])
    my @sorted =
        sort { $a->[0] cmp $b->[0] }
        @decorated;

    # Step 3: Undecorate
    # Extract the original numbers
    for my $pair (@sorted) {
        push @result, $pair->[1];
    }
# ---------------------------------------------------------------    
The advanced, short solution:
# ---------------------------------------------------------------    
# @result = map  { $_->[1] }
#           sort { $a->[0] cmp $b->[0] }
#           map  { [ num2str($_, $lang), $_ ] }
#           @$numbers;
# ---------------------------------------------------------------    
    
    return @result;
}

# Tests

my @numbers = (1, 2, 7, 8, 11, 21);
my $language;

# Example 1 EN
$language = "EN";
printf "%s\n", join(", ", spellbound_sorting_alternative(\@numbers, $language)); # Output: (8, 11, 1, 7, 21, 2)

# Example 2 DE
$language = "DE";
printf "%s\n", join(", ", spellbound_sorting_alternative(\@numbers, $language)); # Output: (8, 1, 21, 11, 7, 2)

# Example 3 NL
$language = "NL";
printf "%s\n", join(", ", spellbound_sorting_alternative(\@numbers, $language)); # Output: (8, 1, 21, 11, 2, 7)

# Example 4 FR
$language = "FR";
printf "%s\n", join(", ", spellbound_sorting_alternative(\@numbers, $language)); # Output: (2, 8, 11, 7, 1, 21)