weird hash sort question

Discussion in 'Perl Misc' started by ccc31807, Dec 3, 2013.

  1. ccc31807

    ccc31807 Guest

    my @teams = ("Florida State","Ohio State","Auburn","Alabama" ...)
    my %ratings;
    #example values of %ratings
    $ratings{BCS}{"Florida State"}{...}{...} = 'some value';
    $ratings{BCS}{"Ohio State"}{...}{...} = 'some value';
    $ratings{BCS}{"Auburn"}{...}{...} = 'some value';
    $ratings{BCS}{"Alabama"}{...}{...} = 'some value';
    $ratings{BCS}{...}{...}{...} = 'some value';
    $ratings{...}{...}{...}{...} = 'some value';

    foreach my $bowl (sort keys %ratings)
    {
    foreach my $team (sort keys %{$ratings->{$bowl}}
    {
    foreach my $somekey (sort keys %{$ratings->{$bowl}{etc}})
    {
    #doesn't matter
    }
    }
    }

    This sort routine gives values like:
    BCS
    Alabama
    Auburn
    Florida State
    Ohio State
    Harris
    etc, etc, and etc.

    WHAT I WANT IS THIS !!!!!
    BCS
    Florida State
    Ohio State
    Auburn
    Alabama
    Harris
    etc, etc, and etc.

    What I'd like to do (but doesn't work) is this:
    foreach my $bowl (sort keys %ratings) # this is okay
    foreach my $team (sort (@teams) keys %{$ratings->{$bowl})
    {
    #okay from here on out
    }
    }

    Suggestions?

    Thanks very, very much, CC.
     
    ccc31807, Dec 3, 2013
    #1
    1. Advertising

  2. * ccc31807 wrote in comp.lang.perl.misc:
    >my @teams = ("Florida State","Ohio State","Auburn","Alabama" ...)


    You can do something like this

    my %team_sortkey = do {
    my $i = 1;
    map { $_ => $i++ } @teams;
    };

    That creates a hash like

    "Florida State" => 1,
    "Ohio State" => 2,
    ...

    Then later sort by comparing the mapped values like

    sort { $team_sortkey{$a} <=> $team_sortkey{$b} } @list_of_teams;

    I believe there are CPAN modules that aid in sorting multi-dimensional
    hashes as well as key-based sorting. For instance, my `List::OrderBy`
    module would let you write

    order_by { $team_sortkey{$_} } @list_of_teams;
    --
    Björn Höhrmann · mailto: · http://bjoern.hoehrmann.de
    Am Badedeich 7 · Telefon: +49(0)160/4415681 · http://www.bjoernsworld.de
    25899 Dagebüll · PGP Pub. KeyID: 0xA4357E78 · http://www.websitedev.de/
     
    Bjoern Hoehrmann, Dec 3, 2013
    #2
    1. Advertising

  3. * Ben Morrow wrote in comp.lang.perl.misc:
    >Quoth :
    >> * ccc31807 wrote in comp.lang.perl.misc:
    >> >my @teams = ("Florida State","Ohio State","Auburn","Alabama" ...)

    >>
    >> You can do something like this
    >>
    >> my %team_sortkey = do {
    >> my $i = 1;
    >> map { $_ => $i++ } @teams;
    >> };


    >> Then later sort by comparing the mapped values like
    >>
    >> sort { $team_sortkey{$a} <=> $team_sortkey{$b} } @list_of_teams;

    >
    >Um, no, there's a *much* easier answer...
    >
    >(Assuming, of course, that @teams is complete. If it isn't it gets a
    >little harder.)


    Iterating over @teams does not sort the keys in the hash, so you are
    more likely to end up with bugs later on without necessarily noticing
    it at first, so I would not necessarily recommend doing that.
    --
    Björn Höhrmann · mailto: · http://bjoern.hoehrmann.de
    Am Badedeich 7 · Telefon: +49(0)160/4415681 · http://www.bjoernsworld.de
    25899 Dagebüll · PGP Pub. KeyID: 0xA4357E78 · http://www.websitedev.de/
     
    Bjoern Hoehrmann, Dec 4, 2013
    #3
  4. Bjoern Hoehrmann <> wrote:
    >* Ben Morrow wrote in comp.lang.perl.misc:
    >>Quoth :
    >>> * ccc31807 wrote in comp.lang.perl.misc:
    >>> >my @teams = ("Florida State","Ohio State","Auburn","Alabama" ...)
    >>>
    >>> You can do something like this
    >>>
    >>> my %team_sortkey = do {
    >>> my $i = 1;
    >>> map { $_ => $i++ } @teams;
    >>> };

    >
    >>> Then later sort by comparing the mapped values like
    >>>
    >>> sort { $team_sortkey{$a} <=> $team_sortkey{$b} } @list_of_teams;

    >>
    >>Um, no, there's a *much* easier answer...
    >>
    >>(Assuming, of course, that @teams is complete. If it isn't it gets a
    >>little harder.)

    >
    >Iterating over @teams does not sort the keys in the hash, so you are


    You can never sort the keys in a hash. At most you can sort the keys of
    a hash.

    >more likely to end up with bugs later on without necessarily noticing
    >it at first, so I would not necessarily recommend doing that.


    And the usual way to sort by a custom criteria is to define a custom
    function that returns -1, 0, or 1 depending upon if the left argument is
    smaller, the same, or a larger than the right argument (aka $a and $b).

    In this case here it appears the OP wants to sort his data by the
    sequence defined in @teams. If so (guessing here, he didn't say so
    explicitely) then we need a custom compare function for those elements.
    The OP can either define that from scratch or leverage the existing
    sequence in @teams using e.g. firstidx() from List::MoreUtils or use one
    of the many other approaches described at
    http://www.perlmonks.org/?node_id=75660.

    While this may not be very efficient it is a well-designed and clean
    approach. If you are looking for a more efficient way then I think
    applying a Schwartzian Transformation may be called for.

    jue
     
    Jürgen Exner, Dec 4, 2013
    #4
  5. Ben Morrow <> writes:
    > Quoth ccc31807 <>:
    >> my @teams = ("Florida State","Ohio State","Auburn","Alabama" ...)

    > [...]
    >>
    >> foreach my $bowl (sort keys %ratings)
    >> {
    >> foreach my $team (sort keys %{$ratings->{$bowl}}

    > [...]
    >>
    >> This sort routine gives values like:
    >> BCS
    >> Alabama
    >> Auburn
    >> Florida State
    >> Ohio State
    >> Harris
    >> etc, etc, and etc.
    >>
    >> WHAT I WANT IS THIS !!!!!
    >> BCS
    >> Florida State
    >> Ohio State
    >> Auburn
    >> Alabama
    >> Harris
    >> etc, etc, and etc.
    >>
    >> What I'd like to do (but doesn't work) is this:
    >> foreach my $bowl (sort keys %ratings) # this is okay
    >> foreach my $team (sort (@teams) keys %{$ratings->{$bowl})
    >> {

    >
    > Stop and think about what you're doing for a minute, instead of just
    > retyping code you've written before. What does
    >
    > keys %{$ratings->{$bowl}}
    >
    > return?


    A set of unknown strings, probably containing 'Florida State', 'Ohio
    State', 'Auburn', 'Alabama' and 'etc, etc. and etc.'.

    > What does sort do to that list?


    It probably reorders it somehow.

    > What will $team be set to each time round the loop?


    To some string.

    > Now, how does the list you have in @teams compare with any of those
    > things?


    It contains strings as well.

    The only sensible question here is "What are you trying to do?" and the
    answer must not be pseudo-Perl not even the author considers to be
    adequate to describe that.
     
    Rainer Weikusat, Dec 4, 2013
    #5
  6. ccc31807

    ccc31807 Guest

    On Tuesday, December 3, 2013 8:15:23 PM UTC-5, Rainer Weikusat wrote:

    > > Stop and think about what you're doing for a minute, instead of just
    > > retyping code you've written before. What does
    > > keys %{$ratings->{$bowl}}
    > > return?

    > A set of unknown strings, probably containing 'Florida State', 'Ohio
    > State', 'Auburn', 'Alabama' and 'etc, etc. and etc.'.
    > > What does sort do to that list?

    > It probably reorders it somehow.
    > > What will $team be set to each time round the loop?

    > To some string.
    > > Now, how does the list you have in @teams compare with any of those
    > > things?

    > It contains strings as well.
    > The only sensible question here is "What are you trying to do?" and the
    > answer must not be pseudo-Perl not even the author considers to be
    > adequate to describe that.


    SOLVED!!!!

    And yeah, I'm kind of excited. I spent most of the afternoon on this instead of working, and got frustrated. My test program and output follows. Comments and criticisms humbly accepted.
    #-------------------sorthash.plx-------------------
    #! perl
    use strict;
    use warnings;
    use List::MoreUtils qw ( indexes );
    use v5.10;

    say qq(sorting hash values.........);
    my @campi = qw( T01 M01 D01 ETROY GC T02);
    foreach (@campi) { print " $_ "; }
    print "\n";

    my %h = (
    M01 => 44,
    T01 => 456,
    ETROY => 342,
    GC => 498,
    T02 => 9,
    D01 => 108,
    );

    foreach my $k (sort keys %h) { say "$k => $h{$k}"; }
    print "-----------------------------\n";
    foreach my $k (sort { $h{$a} <=> $h{$b} } keys %h) { say "$k => $h{$k}"; }
    print "-----------------------------\n";
    foreach my $k (sort {
    my $x = indexes {$_ eq $a} @campi;
    my $y = indexes {$_ eq $b} @campi;
    #print "a $a b $b x $x y $y\n";
    $x > $y ? 1 : -1 ;
    } keys %h) { say "$k => $h{$k}"; }

    exit(0);
    #----------------output--------------
    >perl sorthash.plx

    sorting hash values.........
    T01 M01 D01 ETROY GC T02
    D01 => 108
    ETROY => 342
    GC => 498
    M01 => 44
    T01 => 456
    T02 => 9
    -----------------------------
    T02 => 9
    M01 => 44
    D01 => 108
    ETROY => 342
    T01 => 456
    GC => 498
    -----------------------------
    T01 => 456
    M01 => 44
    D01 => 108
    ETROY => 342
    GC => 498
    T02 => 9
     
    ccc31807, Dec 4, 2013
    #6
  7. >>>>> "RW" == Rainer Weikusat <> writes:

    >> Now, how does the list you have in @teams compare with any of
    >> those things?


    RW> It contains strings as well.

    It contains the keys of the hash, in the order the OP wants them to
    appear in for display.

    Charlton


    --
    Charlton Wilbur
     
    Charlton Wilbur, Dec 4, 2013
    #7
  8. ccc31807 <> writes:

    [...]

    > SOLVED!!!!
    >
    > And yeah, I'm kind of excited. I spent most of the afternoon on this instead of working, and got frustrated. My test program and output follows. Comments and criticisms humbly accepted.
    > #-------------------sorthash.plx-------------------
    > #! perl
    > use strict;
    > use warnings;
    > use List::MoreUtils qw ( indexes );
    > use v5.10;
    >
    > say qq(sorting hash values.........);
    > my @campi = qw( T01 M01 D01 ETROY GC T02);
    > foreach (@campi) { print " $_ "; }
    > print "\n";
    >
    > my %h = (
    > M01 => 44,
    > T01 => 456,
    > ETROY => 342,
    > GC => 498,
    > T02 => 9,
    > D01 => 108,
    > );
    >
    > foreach my $k (sort keys %h) { say "$k => $h{$k}"; }
    > print "-----------------------------\n";
    > foreach my $k (sort { $h{$a} <=> $h{$b} } keys %h) { say "$k => $h{$k}"; }
    > print "-----------------------------\n";
    > foreach my $k (sort {
    > my $x = indexes {$_ eq $a} @campi;
    > my $y = indexes {$_ eq $b} @campi;
    > #print "a $a b $b x $x y $y\n";
    > $x > $y ? 1 : -1 ;
    > } keys %h) { say "$k => $h{$k}"; }


    This is pretty insane coding because it does two linear searches of the
    @campi array for each comparison (To a degree. For a small @campi, this
    might be faster than doing two hash lookups per comparison or at least,
    in can be implemented such that it is. But it's nevertheless quadratic).

    It is possible to build a suitable map/ hash with map like this:

    my %campi = map { $campi[$_], $_ } 0 .. $#campi;

    and then use

    foreach my $k (sort { $campi{$a} <=> $campi{$b} } keys %h) { say "$k => $h{$k}"; }

    Or just use

    for (@campi) { say "$_ => $h{$_}"; }

    NB: Both approaches were suggested already.

    ..
     
    Rainer Weikusat, Dec 4, 2013
    #8
  9. Στις 4/12/2013 00:27, ο/η ccc31807 έγÏαψε:
    > What I'd like to do (but doesn't work) is this:
    > foreach my $bowl (sort keys %ratings) # this is okay
    > foreach my $team (sort (@teams) keys %{$ratings->{$bowl})
    > {
    > #okay from here on out
    > }
    > }
    >
    > Suggestions?
    >
    > Thanks very, very much, CC.
    >



    # this is what you want



    #!/usr/bin/perl
    use strict;
    use warnings;

    my @teams = ("Florida State","Ohio State","Auburn","Alabama");
    my %ratings;


    $ratings{BCS}->{"Ohio State"} =1;
    $ratings{BCS}->{"Florida State"} =1;
    $ratings{BCS}->{"Alabama"} =1;
    $ratings{BCS}->{"Auburn"} =1;


    foreach my $bowl (sort{ SoA($a,\@teams) <=> SoA($b,\@teams) } keys
    %{$ratings{BCS}})
    {
    print "$bowl\n";
    }




    sub SoA
    {
    for (my $i=0; $i < @{$_[1]}; $i++)
    {
    return $i if $_[1]->[$i] eq $_[0]
    }
    }
     
    George Mpouras, Dec 4, 2013
    #9
  10. George Mpouras <> writes:

    [...]

    > sub SoA
    > {
    > for (my $i=0; $i < @{$_[1]}; $i++)
    > {
    > return $i if $_[1]->[$i] eq $_[0]
    > }
    > }


    Perl already knows how to count, consequently, it is not necessary
    to tell it how to do that:

    for (0 .. $#{$_[1]}) {
    return $_ if $_[1]->[$_] eq $_[0];
    }

    or the more compact

    sub ndx {
    $_[1]->[$_] eq $_[0] and return $_ for 0 .. $#{$_[1]};
    }

    This can also be written as

    use List::Util qw(first);

    sub ndx {
    return first { $_[1]->[$_] eq $_[0]} 0 .. $#{$_[1]};
    }

    Although that's IMO not really preferable.
     
    Rainer Weikusat, Dec 4, 2013
    #10
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Navin
    Replies:
    1
    Views:
    704
    Ken Schaefer
    Sep 9, 2003
  2. rp
    Replies:
    1
    Views:
    539
    red floyd
    Nov 10, 2011
  3. Mmcolli00 Mom
    Replies:
    5
    Views:
    139
    Mmcolli00 Mom
    Jan 12, 2009
  4. Tore Aursand
    Replies:
    3
    Views:
    557
    Anno Siegel
    Sep 16, 2003
  5. Malik Yousef

    Sort Hash o Hash accordint to two keys

    Malik Yousef, May 6, 2004, in forum: Perl Misc
    Replies:
    9
    Views:
    197
    Uri Guttman
    May 7, 2004
Loading...

Share This Page