Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Hash sorting (by value) woes

by sschneid (Deacon)
on Feb 24, 2005 at 16:55 UTC ( [id://434134]=perlquestion: print w/replies, xml ) Need Help??

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

Hi again, monks!

I'm having a heck of a time trying to sort a hash. Here are the details:

First off, for reasons uncontrollable by me, I'm being provided a hash containing the contents of /etc/passwd in the following format:

$etcpw = { 'blah' => 'blah:x:3:1:blah:/home/blah:/sbin/nologin', 'tra' => 'tra:x:1:1:tra:/home/tra:/sbin/nologin', 'la' => 'la:x:2:1:la:/home/la:/sbin/nologin' };

My objective is to sort it based on UID, but the following (my best attempt) doesn't quite seem to work:

map { print "$etcpw->{$_}\n"; } sort { ($etcpw->{$a} =~ /:(\d+)/) <=> ($etcpw->{$b} =~ /:(\d+)/) } k +eys %{$etcpw};

I imagine it's probably pretty obvious, but I'm banging my head against the monitor trying to figure it out. Can someone tell me where I'm going wrong?

Thanks,
-s.

Replies are listed 'Best First'.
Re: Hash sorting (by value) woes
by dragonchild (Archbishop) on Feb 24, 2005 at 16:59 UTC
    That's going to evaluate each side of the spaceship operator (<=>) to 1 and make no changes.

    You really want something like:

    map { print "$etcpw->{$_}\n"; } sort { my ($first) = ($etcpw->{$a} =~ /:(\d+)/); my ($second) = ($etcpw->{$b} =~ /:(\d+)/); $first <=> $second } keys %{$etcpw};

    Being right, does not endow the right to be rude; politeness costs nothing.
    Being unknowing, is not the same as being stupid.
    Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
    Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

      That makes sense. Thanks.

      -s.

        Here's an optmization that only executes the regexp once per string:

        @sorted_keys = map { $_->[0] } sort { $a->[1] <=> $b->[1] } map { $etcpw->{$_} =~ /:(\d+)/; [ $_, $1 ] } keys %{$etcpw}; print "$etcpw->{$_}\n" foreach @sorted_keys;

        We can even drop a step:

        print "$etcpw->{$_->[0]}\n" foreach sort { $a->[1] <=> $b->[1] } map { $etcpw->{$_} =~ /:(\d+)/; [ $_, $1 ] } keys %{$etcpw};
Re: Hash sorting (by value) woes
by jonadab (Parson) on Feb 24, 2005 at 17:14 UTC

    This is a good situation for using a Schwartzian Transform. The code you have is close to right, but it's confusing and hard to follow a bit, partly because of all that logic in the sort block that isn't really part of the sorting. I'd move the regex matching out to a preliminary map, put just the sorting code in the sort block, and then map back to the part you want in the upper map...

    my @sortedkeys = map { $$_[0] } sort { $$a[1] <=> $$b[1] } map { $$etcpw{$_} =~ /[:](\d+)/; [$_, $1] } keys %$etcpw;

    Much easier to follow, if you ask me. As far as why your original code doesn't work, I think it's because pattern matches don't do the same thing in scalar context as they do in array context.


    "In adjectives, with the addition of inflectional endings, a changeable long vowel (Qamets or Tsere) in an open, propretonic syllable will reduce to Vocal Shewa. This type of change occurs when the open, pretonic syllable of the masculine singular adjective becomes propretonic with the addition of inflectional endings."  — Pratico & Van Pelt, BBHG, p68
Re: Hash sorting (by value) woes
by RazorbladeBidet (Friar) on Feb 24, 2005 at 17:03 UTC
    Another way (probably a little slower):
    map { print "$etcpw->{$_}\n"; } sort { [ split /:/, $etcpw->{$a} ]->[2] <=> [ split /:/,$etcpw->{$b} + ]->[2] } keys %{$etcpw};
    --------------
    It's sad that a family can be torn apart by such a such a simple thing as a pack of wild dogs
      [ split /:/, $etcpw->{$a} ]->[2]
      can be rewritten as
      (split /:/, $etcpw->{$a})[2]

      Being right, does not endow the right to be rude; politeness costs nothing.
      Being unknowing, is not the same as being stupid.
      Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
      Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://434134]
Approved by Tanktalus
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (4)
As of 2024-03-29 01:35 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found