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

I know how to sort by ascending or descending order just fine. But how do you sort a splitted hash?

$games{person) = "11::Game name::Game date"; I have it setup like that. How can I print the records in order from highest to lowest (first split before :: in the value) and have everything else from that particular heash entry kept together? Ie. I want to print out all scores like:

foreach (sort keys %games) { my ($count, $game, $date) = split(/::/, $games{$_}); print "<td>$_</td> <td>SPLIT BY NUMBER HERE</td>..."; }

Thank you, wise monks.

Replies are listed 'Best First'.
Re: Sorting on a split hash value
by Roger (Parson) on Jan 15, 2004 at 12:59 UTC
    Do you mean something like this...?

    Regular expression with capture
    $games{person} = "11::Game name::Game date"; foreach (sort keys %games) { my ($count, $rest) = $games{$_} =~ /^([^:]+)::(.*)/; print "<td>$count</td><td>$rest</td>..."; }

    Somehow I have a feeling that you are actually looking for a way to sort your hash by each person's score?
    use strict; use warnings; use Data::Dumper; my %games; $games{person1} = "11::Game name::Game date"; $games{person2} = "12::Game name::Game date"; $games{person3} = "9::Game name::Game date"; # sort by score, and record sorted list of hash keys my @sorted = map { $_->[0] } sort { $b->[1] <=> $a->[1] } map { [ $_, (split /::/,$games{$_})[0] ] } keys %games; foreach (@sorted) { # my ($count, $rest) = $games{$_} =~ /^(\d+)::(.*)/; # I like thor's split with limit suggestion below, # certainly looks cleaner. my ($count, $rest) = split/::/,$games{$_},2; print "<td>$_</td><td>$count</td><td>$rest</td>...\n"; }


    And now combile the Schwartzian transform and the foreach to get the optimized form -
    ... print "<td>$_->[1]</td><td>$_->[2]</td>...\n" for sort { int $b->[1] <=> int $a->[1] } map { [ $_, split /::/, $games{$_}, 2 ] } keys %games; ...
      If the delimiter is indeed '::', one can use split and pass in a limit.
      ($count, $rest) = split(/::/,$games{$_},2);

      thor

Re: Sorting on a split hash value
by flounder99 (Friar) on Jan 15, 2004 at 13:40 UTC
    You can do something similar to the Schwartian transform
    use strict; use warnings; my %games = ( abraham => "12::Soccer::1/1/2003", bertha => "10::Pool::3/3/2000", charly => "8::Basketball::5/5/2002", ); for (sort {$b->[0] <=> $a->[0]} map [split(/::/, $games{$_}), $_], keys %games ) { print $_->[-1], " score = ", $_->[0], "\tgame = ", $_->[1], "\ +n"; }
    outputs
    abraham score = 12 game = Soccer bertha score = 10 game = Pool charly score = 8 game = Basketball

    --

    flounder

Re: Sorting on a split hash value
by Hofmator (Curate) on Jan 15, 2004 at 13:14 UTC
    Could you make it a bit clearer what you want to achieve. Let's assume we have a hash like this:
    %games = ( abraham => "12::Soccer::1/1/2003", bertha => "10::Pool::3/3/2000", charly => "8::Basketball::5/5/2002", );
    What is the output you would like to get with this input?

    -- Hofmator

Re: Sorting on a split hash value
by thor (Priest) on Jan 15, 2004 at 13:22 UTC
    If it were me, I'd do something like this (untested):
    @sorted = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { [ $_, /^([^:]+)::/ ] } values %games
    ...which will sort your hash by the first value in the double-colon-separated 'thing' that's stored in your hash. Should you also need to keep track of the key, this example can be modified to suit that purpose.

    HTH,
    thor

Re: Sorting on a split hash value
by hmerrill (Friar) on Jan 15, 2004 at 14:27 UTC
    You really need to try to make your question as clear as possible - I don't really know what you're asking, but here's _my_ guess. You want to know how to sort the %games hash by the "count" part of the value. The only way I know of to sort by part of the value is to get the part(s) of the value you want to sort by, and create a new hash where the "sort" part of the value is the key, and the value is the whole value - something like this:
    #!C:\Perl\bin use strict; my %games = ( 'john' => '11::Boston vs. Orlando::2004-01-15', 'beth' => '20::Minesota vs. New York::2004-01-15', 'bob' => '5::San Antonio vs. Pittsburgh::2004-01-13' ); my %sorted_on_count_hash = (); while (my($person,$value) = each %games) { my ($count,$game,$date) = split /::/, $value; my $sorted_key = sprintf("%04d", $count); # Zero fill count # so that numeric # part of key is same # length, so that sort # is correct based on # count. $sorted_key .= $person; # add person to key to make key unique # for that person $sorted_on_count_hash{$sorted_key} = { "count" => $count, "game" => $game, "date" => $date }; } my %sorted_on_count_hash = (); foreach my $key (sort keys %sorted_on_count_hash) { print "count=[$sorted_on_count_hash{$key}{'count'}], " . "game=[$sorted_on_count_hash{$key}{'game'}], " . "date=[$sorted_on_count_hash{$key}{'date'}]\n"; }
    and here is the output:
    ------------------------------------------------------ count=[5], game=[San Antonio vs. Pittsburgh], date=[2004-01-13] count=[11], game=[Boston vs. Orlando], date=[2004-01-15] count=[20], game=[Minesota vs. New York], date=[2004-01-15] ------------------------------------------------------
    HTH.
Re: Sorting on a split hash value
by Mr. Muskrat (Canon) on Jan 17, 2004 at 02:55 UTC

    If I were doing this and I had the choice, I would come up with a better data structure. Probably something like:

    my %games = ( larry => { count => 11, game => 'Doubles tennis', date => '20040116204235', }, moe => { count => 9, game => 'Singles tennis', date => '20040116203549', }, curly => { count => 1, game => 'Hopscotch', date => '20040116103814', }, );
    That way I could easily add more fields later if I chose to (score perhaps?). It will also make dealing with the information that much easier.

    I'll leave it as an exercise for the Anonymous Monk to get at the data in this HoH.