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

print "<select name=\"m_v\">"; %list = whats(); sub hashValueAscendingNum { $list{$a} <=> $list{$b}; } foreach $key (sort hashValueAscendingNum (keys(%list))) { print "<option value=\"$key\">$list{$key}</option>"; } print "</select>";
This doesnt not print by the has value, and I have no clue why????

Replies are listed 'Best First'.
Re: Sorting by the hash value
by DamnDirtyApe (Curate) on Jul 29, 2002 at 06:02 UTC

    This seems to work OK for me. Can you provide some sample data for %list to illustrate what's going wrong?

    print "<select name=\"m_v\">"; # %list = whats(); my %list = ( 'alpha' => 3, 'bravo' => 5, 'charlie' => 1 ) ; sub hashValueAscendingNum { $list{$a} <=> $list{$b}; } foreach $key (sort hashValueAscendingNum (keys(%list))) { print "<option value=\"$key\">$list{$key}</option>"; } print "</select>";
    Output:
    <select name="m_v"><option value="charlie">1</option><option value="al +pha">3</option><option value="bravo">5</option></select>

    _______________
    D a m n D i r t y A p e
    Home Node | Email
Re: Sorting by the hash value
by Chady (Priest) on Jul 29, 2002 at 06:03 UTC

    This is probably what you want to do:

    foreach my $key ( map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [$_, $list{$_}] } keys %list ) { ...

    See the Schwartzian Transform for more info.

    Update: ok.. so I probably must have added the <joke> tags around ;) I figured it would be obvious... since the guy already has a subroutine to sort in. :).


    He who asks will be a fool for five minutes, but he who doesn't ask will remain a fool for life.

    Chady | http://chady.net/
      Wow! Using a Schwartz Transform to sort a hash by value is like using a sledgehammer to crack open a peanut. The ST is not cheap, it only wins when the cost of computing the key is relatively expensive, and accessing a hash value by key is one of the cheaper things you can do in Perl. I wonder just how much slower this is?

      # /usr/bin/perl -w use strict; use Benchmark qw/cmpthese/; cmpthese( shift || 10_000, { 'sort' => sub { sort keys %ENV }, 'sort by key' => sub { sort {$ENV{$a} cmp $ENV{$b}} keys %ENV }, 'sort by ST' => sub { map { $_->[0] } sort { $a->[1] cmp $b->[1] } map { [$_, $ENV{$_}] } keys %ENV }, }); __END__ % perl -w sortem 100000 Benchmark: timing 100000 iterations of sort, sort by ST, sort by key.. +. sort: 2 wallclock secs ( 1.35 usr + 0.00 sys = 1.35 CPU) @ 73 +964.50/s (n=100000) sort by ST: 18 wallclock secs (17.72 usr + 0.00 sys = 17.72 CPU) @ 56 +44.62/s (n=100000) sort by key: 2 wallclock secs ( 1.34 usr + 0.00 sys = 1.34 CPU) @ 7 +4515.65/s (n=100000) Rate sort by ST sort sort by key sort by ST 5645/s -- -92% -92% sort 73964/s 1210% -- -1% sort by key 74516/s 1220% 1% --

      Yep, pretty slow! (I threw the straight sort in there as well, just to give an idea of the overhead incurred by using a sort code block).


      print@_{sort keys %_},$/if%_=split//,'= & *a?b:e\f/h^h!j+n,o@o;r$s-t%t#u'
        Wow! Using a Schwartz Transform to sort a hash by value is like using a sledgehammer to crack open a peanut.

        This realization ought to have been the clue that guided you to the true purpose of Chady's node. :)

Re: Sorting by the hash value
by grinder (Bishop) on Jul 29, 2002 at 06:05 UTC
    That's odd, because the code works just fine here. Are you sure the routine whats() is returning something? Are you running the code with the -w switch? Are you sure you're not getting any errors, such as "Odd number of elements in hash assignment" or "Use of uninitialized value in concatenation (.) or string"?

    One other thing about your code, if you are generating HTML, you can avoid escaping quotes (i.e., \" ) by using Perl's quoting mechanisms, such as print qq{this is a "quote"\n}. Mind you, there are better ways of generating HTML, but that's another story...


    print@_{sort keys %_},$/if%_=split//,'= & *a?b:e\f/h^h!j+n,o@o;r$s-t%t#u'
Re: Sorting by the hash value
by Juerd (Abbot) on Jul 29, 2002 at 07:23 UTC
Re: Sorting by the hash value
by shotgunefx (Parson) on Jul 29, 2002 at 09:41 UTC
    While this is slightly off from your question. One thing about using CGI.pm that stinks is when you provide a hash so you can have different values and labels (as you are trying to do) it doesn't sort them. If this is why you are avoiding using CGI.pm you can use the snippet below that I use. It's a simple tie that you can use to make it sort by value.
    package SortHash; #Kludge to get options sorted. use Tie::Hash; use vars qw(@ISA); @ISA = qw( Tie::StdHash ); sub FIRSTKEY { my $self = shift; @{ $self->{_SORTHASH_KEYS} } = sort {$self->{$a} cmp $self->{$b} } + grep { $_ ne '_SORTHASH_KEYS' } keys %{$self}; return shift @{ $self->{_SORTHASH_KEYS} }; } sub NEXTKEY { my $self = shift; return shift @{ $self->{_SORTHASH_KEYS} }; } package main; use CGI qw(:standard); my %list; tie %list, 'SortHash' ; # tied hash with sorted keys by value... print start_form; print popup_menu(-name=>'option' , labels=> \%list, -values=> [ keys % +list ] ); print end_form;


    -Lee

    "To be civilized is to deny one's nature."
      "To be civilized is to deny one's nature."
      Denying ones nature is the key to being human.

      Concerning "Sorting by the hash value":
      It is difficult to advise you without knowing exactly what
      you are trying to do, but if you are trying to print a
      sorted list of values couldn't you just:

      @keys = keys{%hash}; foreach $key (@keys) { $value = %hash{$key}; push(@values, $value); } sort(@values); print "@values\n";
        Are you replying to the root node? My kludge is to get around the fact that CGI.pm has the hash ordering (which is no order) when you use the -lables argument. I often need to use labels and tie'ing is easier than sublassing CGI.pm.

        As an aside, your code is overcomplicated.
        print sort values %hash;


        -Lee

        "To be civilized is to deny one's nature."
        Isn't this supposed to be:
        @keys = keys{%hash}; foreach $key (@keys) { $value = $hash{$key}; # notice $ versus % here push(@values, $value); } sort(@values); print "@values\n";

        Otherwise, I think you get an error such as:
        Can't use subscript on private hash at foo.pl line 123, near "$key}" (Did you mean $ or @ instead of %?) BEGIN not safe after errors--compilation aborted at foo.pl line 123.
Re: Sorting by the hash value
by shotgunefx (Parson) on Jul 29, 2002 at 11:17 UTC
    Change the sort to
    sort { $list{$a} <=> $list{$b} || $list{$a} cmp $list{$b} } keys %list
    or compacted...
    print "<select name=m_v>\n", ( map {"\t<option name=\"$_\">$list{$_}\n" } sort { $list{$a} <=> $list{$b} || $list{$a} cmp $list{$b} } + keys %list ), '</select>';


    -Lee

    "To be civilized is to deny one's nature."