danj35 has asked for the wisdom of the Perl Monks concerning the following question:

Hi

Been stuck on this small problem for a while now and wondered if anyone can help. I have a hash where the keys all begin with E followed by a number, e.g. E47. I need to sort through this hash so that it goes E1, E2... E59 and so on, while it iterates through the foreach loop. The best I can think of is this, but it only compares the second char.

foreach my $key (sort { $a cmp $b } keys %full){ }

I imagine there's a simple solution, any help would be greatly recieved.

Thanks

Replies are listed 'Best First'.
Re: sort after first char of hash key
by wind (Priest) on Jun 02, 2011 at 14:48 UTC

    Just use substr to extract the proper values to sort by

    my @list = map {"E$_"} (1..20); for my $k (sort {substr($a, 1) <=> substr($b, 1)} @list) { print "$k "; }

    Note: If your keys don't actually all start with E, but you instead want to sort first by the alpha section and then numerically by the integer suffix, then you simply must extract the sections before sorting like so:

    my @list = ("A15", "Y2", map {"E$_"} (1..20)); for my $k ( map {$_->[0]} sort {$a->[1] cmp $b->[1] || $a->[2] <=> $b->[2]} map {[$_, /(\D+)(\d+)/]} @list ) { print "$k "; }

      Excellent suggestion and one I should have thought of before. It is near the end of the week...

Re: sort after first char of hash key
by Cristoforo (Curate) on Jun 03, 2011 at 00:38 UTC
    Here is the Guttman-Rosler Transform.

    #!/usr/bin/perl use strict; use warnings; use 5.012; use List::Util qw/ shuffle /; srand(1); # to get same random 'shuffle' from run to run my @list = shuffle (qw/ Y2 A15 YABBA287 DABBA345 DOO777/, map {"E$_"} +(1..20)); say "UNSORTED"; say "@list"; my @sorted = map { my $strlength = unpack("C", substr($_, -1)); substr $_, -1 * (1 + $strlength), -1; } sort map { pack "a*Na*C", /(\D+)(\d+)/, $_, length} @list; say "SORTED"; say "@sorted";

    The advantage of this sort is that it uses a simple alphabetical sort. Its is usually the fastest performing sort. The configuration here will sort any 'alpha'+'number' string, not just a single leading letter plus a number.

    Update: The setup here is limited to strings of length <= 255. If the strings to be sorted are longer than that, you would have to make some changes. Also, to make the sort case insensitive, which is usually the desired outcome, the first map could be restated as:

    map { pack "a*Na*C", (map {lc} /(\D+)(\d+)/), $_, length} @list;

    by adding the lc operator.

Re: sort after first char of hash key
by thundergnat (Deacon) on Jun 02, 2011 at 18:18 UTC

    Natural sort the keys

    sub natural_sort { my $i; s/(\d+)/pack 'aNa*', 0, length $1, $1/eg, $_ .= ' ' . $i++ for ( m +y @x = @_ ); @_[ map { (split)[-1] } sort @x ]; } print join ', ', natural_sort( map {"E$_"} (1..20) );