Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

How to sort the data in Array which has format MMYY

by kum@12 (Initiate)
on Jul 02, 2013 at 07:04 UTC ( #1041970=perlquestion: print w/ replies, xml ) Need Help??
kum@12 has asked for the wisdom of the Perl Monks concerning the following question:

Hi Experts,

Trying to sort the array which has a date in MMMYY format, i have given below input and my code

The array contains data as below:

APR12 MAR13 APR11 MAR12 FEB13 APR13
#!/usr/bin/env perl my (@months) = qw(APR12 MAR13 APR11 MAR12 FEB13 APR13); my @mon = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ); my $mon; @{$mon}{@mon} = ( 0 .. $#mon ); sub by_month { $mon->{$a} <=> $mon->{$b}; } @months = ( sort by_month @months ); print "@months\n";

gives me this error message / faulty output:

APR12 MAR13 APR11 MAR12 FEB13 APR13

I am new to perl, any ideas on this, where i am wrong

Comment on How to sort the data in Array which has format MMYY
Select or Download Code
Re: How to sort the data in Array which has format MMYY
by Corion (Pope) on Jul 02, 2013 at 07:16 UTC

    If you had used warnings or ran your script as perl -w 1041970.pl, Perl would've given you a hint on what goes wrong:

    Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. Use of uninitialized value in numeric comparison (<=>) at tmp.pl line +10. APR12 MAR13 APR11 MAR12 FEB13 APR13

    Now, the task is to find out why $mon->{$a} might be undef. You could for example do a first simple sanity check:

    ... use Data::Dumper; exists $mon->{JAN} or die "Couldn't find entry for 'JAN' in " . Dumper $mon;

    That should give you a hint on how to proceed.

Re: How to sort the data in Array which has format MMYY
by tobyink (Abbot) on Jul 02, 2013 at 07:25 UTC

    Firstly, the months in your hash lookup are mixed case, while in your data they're uppercase. Hash keys are case sensitive.

    Secondly, in your by_month function you're getting strings like "APR12" and looking them up in a hash with keys like "Apr". You need to strip the number off before looking them up in the hash.

    Here's a fixed version of your script:

    #!/usr/bin/env perl my (@months) = qw(APR12 MAR13 APR11 MAR12 FEB13 APR13); my @mon = map uc, qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec +); my $mon; @{$mon}{@mon} = ( 0 .. $#mon ); sub by_month { my ($a_mon, $a_yy) = ($a =~ /(...)(..)/); my ($b_mon, $b_yy) = ($b =~ /(...)(..)/); $mon->{$a_mon} <=> $mon->{$b_mon}; } @months = ( sort by_month @months ); print "@months\n";

    Note that the above completely ignores the years though. If you want to sort by year first and then month, you could use:

    #!/usr/bin/env perl my (@months) = qw(APR12 MAR13 APR11 MAR12 FEB13 APR13); my @mon = map uc, qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec +); my $mon; @{$mon}{@mon} = ( 0 .. $#mon ); sub by_month { my ($a_mon, $a_yy) = ($a =~ /(...)(..)/); my ($b_mon, $b_yy) = ($b =~ /(...)(..)/); $a_yy <=> $b_yy or $mon->{$a_mon} <=> $mon->{$b_mon}; } @months = ( sort by_month @months ); print "@months\n";

    If you've got a lot of data, the speed could be improved via a Schwartzian transform, but as you're new to Perl, I won't get into that. ;-)

    package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow->new->name
      Refer to the FAQ perldoc -q "How do I sort an array" for details on Schwartzian Transform.
      Bill
      Hi All,

      When i try to sort the input data, i am not getting the wrong results

      #!/usr/bin/env perl my (@months) = qw(Mar13 Jun13 Jul13 Apr13 ); my @mon = map uc, qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec +); my $mon; @{$mon}{@mon} = ( 0 .. $#mon ); sub by_month { my ($a_mon, $a_yy) = ($a =~ /(...)(..)/); my ($b_mon, $b_yy) = ($b =~ /(...)(..)/); $a_yy <=> $b_yy or $mon->{$a_mon} <=> $mon->{$b_mon}; } @months = ( sort by_month @months ); print "@months\n";

      output

      Mar13 Jun13 Jul13 Apr13

      please help me on this</P<

        If your @months are no longer upper case, drop the uc.
        لսႽ ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: How to sort the data in Array which has format MMYY
by hdb (Prior) on Jul 02, 2013 at 08:10 UTC

    You could turn the format from MMMYY into YYMM and then sort numerically. The following sub also takes care or the capitalization issue and fits into your original post:

    sub by_month { ($a=~/(...)(..)/,sprintf('%02d%02d',$2,$mon->{ucfirst lc $1})) <=> ($b=~/(...)(..)/,sprintf('%02d%02d',$2,$mon->{ucfirst lc $1})); }
      Hi Masters,

      Thanks for the help, please suggest me as i am new to perl programming, where can i learn the perl from basics, any links or online books

Re: How to sort the data in Array which has format MMYY
by Anonymous Monk on Jul 02, 2013 at 14:05 UTC

    New to perl because you of time of practice or things you don't know? Whichever, you still have to make time to know the right tools for your job.
    Without taking anything away from the wise monks that has posted here before now, you can use Schwartzian transform like so:

    use warnings; use strict; my @mon_yr = qw/ APR12 MAR13 APR11 MAR12 FEB13 APR13/; my %mon_name = ( 'JAN' => 1, 'FEB' => 2, 'MAR' => 3, 'APR' => 4, # add others if you want ); print join $/ => map { $_->[0] } sort { $a->[2] <=> $b->[2] || $a->[1] <=> $b->[1] } map { /(\w+?)(\d+?)$/; [ $_, $mon_name{$1}, $2 ] } @mon_yr; print $/;
    Schwartzian transform is simply using "map" and "sort" from perl is nothing difficult..... you see :)

Re: How to sort the data in Array which has format MMYY
by sundialsvc4 (Abbot) on Jul 02, 2013 at 14:14 UTC

    As you know, the sort verb takes as one argument a subroutine (or an in-line block of code) whose job it is to return a value that is less than, equal to, or greater than zero when given two keys to compare.   It can be a separate subroutine, and I like to do it that way, especially when I have several occurrences of the same sort.

    When dealing with date-values, I prefer to use a nice “date/time object” such as DateTime, which relieves me of all concerns about date/time handling.

    But, when you can’t do that, you can do sorting of months by defining a hashref that gives you numeric equivalents for them.   So, your sorting subroutine becomes something like this:   (untested)

    my %month_sort = ( 'JAN' => 1, 'FEB' => 2, 'MAR' => 3, 'APR' => 4, 'MAY' => 5, 'JUN' => 6, ... etcetera ... ); sub sort_cmp { my ($a, $b) = @_; return ( substr($a, 3, 2) cmp substr($b, 3, 2) ) || ( $month_sort->{substr($a, 0, 3)} <=> $month_sort->{substr($b, 0, 3)} ); }

      Maybe when replying to somebody who already says is new to perl, it would be nice to actually test your code.

      Problems:

      1) don't pass $a and $b to a sort sub, since they are special variables

      2) don't treat a hash like a hashref, it just won't work

      3) you don't need the return either

      This does work:
      use strict; use warnings; my %month_sort = ( 'JAN' => 1, 'FEB' => 2, 'MAR' => 3, 'APR' => 4, 'MAY' => 5, 'JUN' => 6, 'JUL' => 7, 'AUG' => 8, 'SEP' => 9, 'OCT' => 10, 'NOV' => 11, 'DEC' => 12 ); sub sort_cmp { ( substr($a, 3, 2) cmp substr($b, 3, 2) ) || ( $month_sort{substr($a, 0, 3)} <=> $month_sort{substr($b, 0, 3)} ); } my (@months) = qw(APR12 MAR13 APR11 MAR12 FEB13 APR13); @months = ( sort sort_cmp @months); print "@months \n";

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (12)
As of 2014-10-01 19:02 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    What is your favourite meta-syntactic variable name?














    Results (34 votes), past polls