laziness, impatience, and hubris PerlMonks

### How to sort hash tables alpha-numeric

by juo (Curate)
 on Oct 18, 2003 at 05:27 UTC Need Help??
juo has asked for the wisdom of the Perl Monks concerning the following question:

```\$keys{'C10'}->{'C10-1'}=1;
\$keys{'C10'}->{'C10-2'}=1;
\$keys{'C10'}->{'C10-20'}=1;
\$keys{'C10'}->{'C10-3'}=1;

\$keys{'C200'}->{'C200-1'}=1;
\$keys{'C200'}->{'C200-20'}=1;
\$keys{'C200'}->{'C200-3'}=1;
\$keys{'C200'}->{'C200-21'}=1;

\$keys{'C30'}->{'C30-1'}=1;
\$keys{'C30'}->{'C30-2'}=1;
\$keys{'C30'}->{'C30-30'}=1;
\$keys{'C30'}->{'C30-4'}=1;

# Result should be :

'C10' => HASH(0x1832a58)
'C10-1' => 1
'C10-2' => 1
'C10-3' => 1
'C10-20' => 1
'C30' => HASH(0x1832b30)
'C30-1' => 1
'C30-2' => 1
'C30-4' => 1
'C30-30' => 1
'C200' => HASH(0x1835318)
'C200-1' => 1
'C200-3' => 1
'C200-20' => 1
'C200-21' => 1

Replies are listed 'Best First'.
Re: How to sort hash tables alpha-numeric
by Aragorn (Curate) on Oct 18, 2003 at 17:48 UTC
Here's a solution using the so-called Schwartzian transform for sorting the keys (the leading "C" is stripped off, and the the key list is sorted by the number before the dash, and then by the number after the dash:
```#!/usr/bin/perl

use warnings;
use strict;

my %keys;

\$keys{'C10'}->{'C10-1'}=1;
\$keys{'C10'}->{'C10-2'}=1;
\$keys{'C10'}->{'C10-20'}=1;
\$keys{'C10'}->{'C10-3'}=1;

\$keys{'C200'}->{'C200-1'}=1;
\$keys{'C200'}->{'C200-20'}=1;
\$keys{'C200'}->{'C200-3'}=1;
\$keys{'C200'}->{'C200-21'}=1;

\$keys{'C30'}->{'C30-1'}=1;
\$keys{'C30'}->{'C30-2'}=1;
\$keys{'C30'}->{'C30-30'}=1;
\$keys{'C30'}->{'C30-4'}=1;

sub sorted {
return map { \$_->[0] }
sort {
\$a->[1] <=> \$b->[1] ||
\$a->[2] <=> \$b->[2]
} map { [ \$_, split("-", substr(\$_,1)) ] } @_;
}

foreach my \$key (sorted(keys %keys)) {
print "'\$key' => ", \$keys{\$key}, "\n";
foreach my \$subkey (sorted(keys %{\$keys{\$key}})) {
print "   '\$subkey' => \$keys{\$key}->{\$subkey}\n";
}
}

Arjen

This works perfect although I don't understand yet 100% how the Schwartzian transform works. Their is one small problem that I have with the code is that it should look for all leading characters to be split because now it substr only one but it could be more. (CR30-1 for example) So I tried to modify the code but no success.
```sub sorted {
return map { \$_->[0] }
sort {
\$a->[1] <=> \$b->[1] ||
\$a->[2] <=> \$b->[2]
} map { [ \$_, split("-", s/^[A-Z]+//) ] } @_;
}

foreach my \$key (sorted(keys %keys)) {
print "'\$key' => ", \$keys{\$key}, "\n";
foreach my \$subkey (sorted(keys %{\$keys{\$key}})) {
print "   '\$subkey' => \$keys{\$key}->{\$subkey}\n";
}
}
The Schwartzian Transform is explained here by the person after who this sorting technique is named.

A modified version of the sorted routine which also takes the leading letters into account:

```
sub sorted {
return map { \$_->[0] }
sort {
\$a->[1] cmp \$b->[1] ||
\$a->[2] <=> \$b->[2] ||
\$a->[3] <=> \$b->[3]
} map { [ \$_, (/([A-Z]+)(\d+)(?:-(\d+)){1,2}/) ] } @_;
}
The split function is replaced by a regex which returns a list of letters and the numbers.

Arjen

Create A New User
Node Status?
node history
Node Type: perlquestion [id://300236]
Approved by davido
help
Chatterbox?
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (2)
As of 2018-05-22 01:41 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?
World peace can best be achieved by:

Results (163 votes). Check out past polls.

Notices?