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

Hi monks, i'm looking for a way to do sorts in a non-numerical and numerical way...
this is the code i've got so far but it doesnt handle non-numerical items.
#!/usr/bin/perl use strict; use warnings; my %item1 = ( "item" => 1, "name" => "smith, john", "age" => 25, "height" => 1.80 ); my %item2 = ( "item" => 2, "name" => "jones, bob", "age" => 26, "height" => 1.70 ); my %item3 = ( "item" => 3, "name" => "arrang, eliee", "age" => 27, "height" => 1.75 ); my %hash; $hash{1} = { %item1 }; $hash{2} = { %item2 }; $hash{3} = { %item3 }; my $orderby = "name"; foreach my $id (sort { $hash{$b}{$orderby} <=> $hash{$a}{$orderby} } k +eys %hash) { print "$hash{$id}{'item'} - $hash{$id}{$orderby}\n"; } # end-foreach exit;
Can anyone help me out?

Cheers,
Reagen

Replies are listed 'Best First'.
Re: numerical and non numerical sorts
by Zaxo (Archbishop) on Oct 06, 2005 at 12:22 UTC

    Numeric sorts are done with the <=> operator, string sorts with cmp.

    Numeric sort is putting those strings in numeric context where they all evaluate to zero. If you must accomodate both kinds of sort, you could almost use the all-zero property to say

    for my $i (sort { $hash{$b}{$orderby} <=> $hash{$a}{$orderby} || $hash{$b}{$orderby} cmp $hash{$a}{$orderby} } keys %hash) { # do stuff }
    but that would fail, for instance, for strings which look like numbers to perl. It would also sort string representations of numbers according to string rules only if they were numerically the same ( "023" and "2.3e1" would be sorted with "023" first).

    If you know enough about your data, that sort rule may be good enough.

    After Compline,
    Zaxo

Re: numerical and non numerical sorts
by Roy Johnson (Monsignor) on Oct 06, 2005 at 12:40 UTC
Re: numerical and non numerical sorts
by Fang (Pilgrim) on Oct 06, 2005 at 12:26 UTC

    It's hard to properly understand on what criteria you want to sort based on your description. As you want to order by the value of the 'name' field (which is non-numerical), you should use the cmp operator instead of <=> in your sort.

    Now, if what you want to do is order by a non-numerical field, and then in case of equality fall back to comparing numerical fields (like the 'age' field for example), the usual way is to declare a little sub you'll use with the sort function.

    for my $id (sort by_name_and_age keys %hash) { print "$hash{$id}{'item'} - $hash{$id}{'name'}\n"; } sub by_name_and_age { # Sort by reversed ASCII order... $hash{$b}{'name'} cmp $hash{$a}{'name'} or # ... or by ascending age value. $hash{$a}{'age'} <=> $hash{$b}{'age'}; }

    As always, it's a good idea to read (again) the documentation of sort.

Re: numerical and non numerical sorts
by Skeeve (Parson) on Oct 06, 2005 at 12:47 UTC
    ugly, but this will do:
    1. add a sort function hash
      my %sortfkt = ( "item" => \&numerical, "name" => \&string, "age" => \&numerical, "height" => \&numerical, );
    2. add the functions
      sub string { return $_[0] cmp $_[1]; } sub numerical { return $_[0] <=> $_[1]; }
    3. change the sort
      sort { &{$sortfkt{$orderby}}($hash{$a}{$orderby}, $hash{$b}{$orderby}) + } keys %hash
    putting it together:

    $\=~s;s*.*;q^|D9JYJ^^qq^\//\\\///^;ex;print
Re: numerical and non numerical sorts
by Perl Mouse (Chaplain) on Oct 06, 2005 at 12:26 UTC
    Using <=> will compare numerically, using cmp compare lexicographically. But you have to tell Perl how you want to sort - Perl cannot magically find out how you want to sort.
    Perl --((8:>*
Re: numerical and non numerical sorts
by Anonymous Monk on Oct 07, 2005 at 10:51 UTC
    perldoc -f sort

    has examples