Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

sorting keys in hash

by Perlseeker_1 (Acolyte)
on May 23, 2014 at 15:55 UTC ( [id://1087227]=perlquestion: print w/replies, xml ) Need Help??

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

Hi Experts,

I have formed a hash with month (mmmyy) as a key and value (numeric), and I want the output in sorted format

my code is below

I am trying print the out put as

my %data foreach my $key (sort keys %data) { print "Key - $key and b value - $data{$key}}

Hash conetent

$VAR1 = 'Dec13'; $VAR2 = '2'; $VAR3 = 'May14'; $VAR4 = '2'; $VAR5 = 'Apr14'; $VAR6 = '2'; $VAR7 = 'Mar14'; $VAR8 = '2';

but I am getting output as:

key - Apr14 and value - 2 key - Dec13 and value - 2 key - Mar14 and value - 2 key - May14 and value - 2

where I want the output in sorted format

key - Dec13 and value - 2 key - Mar14 and value - 2 key - Apr14 and value - 2 key - May14 and value - 2

Any suggestions please

Replies are listed 'Best First'.
Re: sorting keys in hash
by Laurent_R (Canon) on May 23, 2014 at 17:28 UTC
    Why don't you change your keys to something that sorts correctly naturally, such as 13-12 instead of dec13 and 14-01 instead of jan14? It is so much easier.
Re: sorting keys in hash
by johngg (Canon) on May 23, 2014 at 22:17 UTC

    Using split, a hash for month sort order look-ups and a ST seems simplest.

    $ perl -Mstrict -Mwarnings -E ' my %mthSO = do { my $order; map { $_, ++ $order } qw{ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec }; }; say for map { $_->[ 0 ] } sort { $a->[ 2 ] <=> $b->[ 2 ] || $mthSO{ $a->[ 1 ] } <=> $mthSO{ $b->[ 1 ] } } map { [ $_, split m{ (?<= [a-z] ) (?= \d ) }x ] } qw{ May14 Dec13 Mar14 Oct12 Apr14 };' Oct12 Dec13 Mar14 Apr14 May14 $

    I hope this is helpful.

    Update: Corrected lame-brain wrong comparator for month, thanks AnomalousMonk ++

    Cheers,

    JohnGG

      my %mthSO = do { my $order; map { $_, ++ $order } qw{ ... }; };
      ...
      $mthSO{ $a->[ 1 ] } cmp $mthSO{ $b->[ 1 ] }

      Since month names are map-ped to numeric values, shouldn't the month name value comparison be numeric  <=> rather than lexicographic? Adding the dates  Jan14 Dec14 to the test set shows the problem.

        Yep, corrected, lamentable senior moment :-(

        Cheers,

        JohnGG

Re: sorting keys in hash
by LanX (Saint) on May 23, 2014 at 16:01 UTC
    The output IS sorted (alphabetically) while your desired order is rather cryptic and I don't want to speculate (its not chronological)

    Plz try to express in words which kind of sorting you really want.

    Cheers Rolf

    ( addicted to the Perl Programming Language)

      Hi,

      I want the output in MMMYY format, the present output is alphabetically sorted but I am trying to print output in MMMYY format sorted way

      Expected Output:

      key - Dec13 and value - 2 key - Mar14 and value - 2 key - Apr14 and value - 2 key - May14 and value - 2
        So that means you must write your own comparison subroutine, as described in sort. You'll also need to specify ordering on months, since MMM format doesn't naturally sort. Something like:
        my %MMM_order = ( Mar => 3, Apr => 4, May => 5, Dec => 12, ); sub cmp_MMMYY { my ($a_mon, $a_yr) = $a =~ /(.{3})(\d{2})/; my ($b_mon, $b_yr) = $b =~ /(.{3})(\d{2})/; $a_yr cmp $b_yr or $MMM_order{$a_mon} <=> $MMM_order{$b_mon}; }

        Given the content of your post convert date format from YYYY-MM-DD to YYYYMMDD, I should mention that this sort would be much more natural in ISO 8601, and there are conversion methods discussed in the response to that post.


        #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

        You are only repeating not explaining. :(

        I'm out! :)

        update

        If 13 means the year and not the day this could be indeed chronological. (?)

        Cheers Rolf

        ( addicted to the Perl Programming Language)

Re: sorting keys in hash
by farang (Chaplain) on May 23, 2014 at 23:08 UTC

    Yet another way, though I totally agree that a much better solution would be to use an inherently sortable format for the hash keys from the start.

    use strict; use warnings; use feature 'say'; my %data = ( Apr14 => 2, Mar14 => 2, May14 => 2, Dec13 => 2, ); my %month_to_num = ( Jan => '01', May => '05', Sep => '09', Feb => '02', Jun => '06', Oct => '10', Mar => '03', Jul => '07', Nov => '11', Apr => '04', Aug => '08', Dec => '12', ); my %num_to_month = reverse %month_to_num; say "key - $_ and value - $data{$_}" for map { s/ (\d{2}) (\d{2}) /$num_to_month{$2}$1/rx } sort map { s/ (\w{3}) (\d{2}) /$2$month_to_num{$1}/rx } keys %data;

      If you're going to go to the trouble of two  s/// operations, you might as well go all the way to a GRT/decorate-sort-undecorate solution; then you can at least claim to be getting the maximum possible performance from the Perl sort built-in.

      c:\@Work\Perl\monks>perl -wMstrict -le "my %data = qw( Dec02 2 Jan02 3 May05 4 Dec14 8 Dec01 5 Jan14 7 Apr05 6 Feb14 9 May15 10 ); ;; use constant Y_WID => 2; use constant M_WID => 3; my %mon_n = qw( Jan 1 Feb 2 Mar 3 Apr 4 May 5 Jun 6 Jul 7 Aug 8 Sep 9 Oct 10 Nov 11 Dec 12 ); ;; my @sorted_dates = map unpack(qq{x${\Y_WID} x${\M_WID} a*}, $_), sort map sprintf('%*s%*s%s', Y_WID, $_->[1], M_WID, $mon_n{$_->[0]}, $_- +>[2]), map [ m{ \A ([[:alpha:]]{${\M_WID}}) (\d{${\Y_WID}}) \z }xms, $_ ], keys %data ; ;; print qq{Key - $_ and b value - $data{$_}} for @sorted_dates; " Key - Dec01 and b value - 5 Key - Jan02 and b value - 3 Key - Dec02 and b value - 2 Key - Apr05 and b value - 6 Key - May05 and b value - 4 Key - Jan14 and b value - 7 Key - Feb14 and b value - 9 Key - Dec14 and b value - 8 Key - May15 and b value - 10

      Update: See A Fresh Look at Efficient Perl Sorting by Guttman and Rosler (the G and R of GRT). Also: It seems to me now that all the regex sub-string extraction and sprintf-ing would have been much neater, clearer and more maintainable if encapsulated into a separate function. Oh, well...

Re: sorting keys in hash
by dbuckhal (Chaplain) on May 26, 2014 at 00:17 UTC

    Took me a while to come full circle to this, but was trying crazy stuff with splice after moving keys to array! ABORT!!!

    So, I realized that once months were translated to numbers, concatenating the day and month in reverse would be easily sortable:

    Jul11 = 1107 Jun11 = 1106 Aug13 = 1308 May14 = 1405 etc...

    Update

    Looked up s///e in the Camel Book and was able to shorten the sortKeys function from:
    sub sortKeys { my ( $l_mo, $l_yr ) = $a =~ /(\D+)(\d+)/; my ( $r_mo, $r_yr ) = $b =~ /(\D+)(\d+)/; my $left = $l_yr . $months->{$l_mo}; my $right = $r_yr . $months->{$r_mo}; return ( $left cmp $right ); }
    to:
    sub sortKeys { ( my $left = $a ) =~ s/(\D+)(\d+)/$2 . $months->{$1}/e; ( my $right = $b ) =~ s/(\D+)(\d+)/$2 . $months->{$1}/e; return ( $left cmp $right ); }

    Modified code below:

    #!/usr/bin/perl # http://www.perlmonks.org/?node=1087227 use strict; use warnings; my $months = { "Jan" => "01", "Feb" => "02", "Mar" => "03", "Apr" => "04", "May" +=> "05", "Jun" => "06", "Jul" => "07", "Aug" => "08", "Sep" => "09", "Oct" => "10", "Nov" +=> "11", "Dec" => "12", }; my $data = { "Jul11" => 2, "Dec13" => 2, "Jan14" => 2, "Aug13" => 2, "Jun11" => 2, "Apr13" => 2, "Mar12" => 2, "May14" => 2, }; # using regular sort print "before:\n"; foreach my $key (sort keys %$data) { print "Key - $key and b value - $data->{$key}\n"; } # using sortKeys sort print "after\n"; foreach my $key ( sort { sortKeys( $a, $b ) } keys %$data ) { print "Key - $key and b value - $data->{$key}\n"; } sub sortKeys { ( my $left = $a ) =~ s/(\D+)(\d+)/$2 . $months->{$1}/e; ( my $right = $b ) =~ s/(\D+)(\d+)/$2 . $months->{$1}/e; return ( $left cmp $right ); } __output__ $ ./node_id_1087227_hashkey_sort.pl before: Key - Apr13 and b value - 2 Key - Aug13 and b value - 2 Key - Dec13 and b value - 2 Key - Jan14 and b value - 2 Key - Jul11 and b value - 2 Key - Jun11 and b value - 2 Key - Mar12 and b value - 2 Key - May14 and b value - 2 after Key - Jun11 and b value - 2 Key - Jul11 and b value - 2 Key - Mar12 and b value - 2 Key - Apr13 and b value - 2 Key - Aug13 and b value - 2 Key - Dec13 and b value - 2 Key - Jan14 and b value - 2 Key - May14 and b value - 2

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (4)
As of 2024-04-25 20:17 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found