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

Using a hash containing arrays, I want to print out a list sorted over all array elements while maintaining the hash entry name.

To illustrate, I borrowed an example from perldoc/perldsc on Hashes of Arrays

In the last part of the example I can print out the list by creating a temporary list of all the names in the arrays, then sort and print it. However, at that point I no longer have the "family name" hash entry association. So, I then re-iterate through the hash and arrays to match the sorted members list with their original hash entries and print from there.

What I want is something like this:

astro:jetsons, barney:flinstones, bart:simpsons, elroy:jetsons, fred:flintstone, ...

BTW: normally sorting by hash key (and then possibly within an array) is what I want, so I "think" I have the correct data structure. However, sometimes I will want to sort over all items in all arrays.

The code below works but I just can't help to think that there's an easier or better way to do it.
Any help finding the better way is appreciated.

Thanks

#!/usr/bin/perl -w use strict; use Data::Dumper; my %HoA = ( simpsons => [ "homer", "marge", "bart" ], flintstones => [ "fred", "barney" ], jetsons => [ "george", "jane", "elroy", "astro" ], ); # # This is the type of sorting normally needed. # print "Sorted by number of members then name\n"; foreach my $family (sort {@{$HoA{$b}} <=> @{$HoA{$a}} || $a cmp $b } k +eys %HoA){ print " $family: \t", join(", ", sort @{ $HoA{$family} }), "\n"; } # # Sometimes this sort will be needed. # my @members; # Dump all names into a single array foreach my $family (keys %HoA ) { push (@members, @{$HoA{$family}} ); } #Sort it and find it's family association for printing foreach my $name (sort @members) { foreach my $family (keys %HoA) { foreach my $test (@{$HoA{$family}}) { print " $name : $family\n" if ($test eq $name); } } }

Replies are listed 'Best First'.
Re: Hash of Arrays - Sort over all array entries (flatten first)
by tye (Sage) on Sep 09, 2003 at 21:49 UTC

    Just flatten the list first (formatting the items so they sort properly), sort, then reformat how you would like to output them (which isn't even required in this case):

    my %HoA = ( simpsons => [ "homer", "marge", "bart" ], flintstones => [ "fred", "barney" ], jetsons => [ "george", "jane", "elroy", "astro" ], ); my @members= sort map { my $key= $_; map " $_ : $key", @{$HoA{$key}}; } keys %HoA; print join $/, @members, ""; __END__
    Produces:
    astro : jetsons barney : flintstones bart : simpsons elroy : jetsons fred : flintstones george : jetsons homer : simpsons jane : jetsons marge : simpsons

                    - tye
Re: Hash of Arrays - Sort over all array entries
by Abigail-II (Bishop) on Sep 09, 2003 at 21:37 UTC
    #!/usr/bin/perl -l use strict; use warnings; no warnings qw /syntax/; my %HoA = ( simpsons => [qw /homer marge bart/], flintstones => [qw /fred barney/], jetsons => [qw /george jane elroy astro/], ); print for sort map {my $name = $_; map {"$_:$name"} @{$HoA {$name}}} k +eys %HoA;

    Of course, whether this is 'easier' is in the eye of the beholder.

    Abigail

      Thanks Abigail-II and tye for the info. This method reduces the number of times through loops from 4 7 in my case, to 2(?) 5.

      Of course, whether this is 'easier' is in the eye of the beholder.

      Being new to perl I really have to stare at the method above to get the sense of it (I really haven't used map much). So, indeed 'easier' does depend on what you are looking for.

      Thanks again.

      Update: Changed faulty # of loops :-)

        <bq> This method reduces the number of times through loops from 4 in my case, to 2(?). </bq>

        Actually, I count 5 loops: keys, map, map, sort, for.

        Abigail

        This method reduces the number of times through loops

        The number of loops is usually less important than how they are nested. 5 loops, none of them nested means run-time on the order of O(5*$n) which is O($n). You had three of your loops nested which is more like O($n**3).

        In this case, the loop sizes are more like $n (number of families), $m (number of members per family), and $n*$m (total number of members) so we go from O( ($n*$m)**2 ) to just O($n*$m).

                        - tye