tags:

views:

237

answers:

3

How can I sort the dates in Perl?

my @dates =  ( "02/11/2009" , "20/12/2001" , "21/11/2010" ); 

I have above dates in my array . How can I sort those dates?

My date format is dd/mm/YYYY.

+10  A: 
@dates = sort { join('', (split '/', $a)[2,1,0]) cmp join('', (split '/', $b)[2,1,0]) } @dates;

or using separate sorting subroutine:

sub mysort {
    join('', (split '/', $a)[2,1,0]) cmp join('', (split '/', $b)[2,1,0]);
}
@dates = sort mysort @dates;

Update: A more efficient approach is the Schwartzian Transform:

@dates = 
    map $_->[0],
    sort { $a->[1] cmp $b->[1] }
    map  [ $_, join('', (split '/', $_)[2,1,0]) ], @dates;
eugene y
Superb...Thanks a lot....
kiruthika
Do this with a Schwartzian Transform or other cached key sort so you don't have to recompute the values every time.
brian d foy
@brian: Thanks, I forgot about this :-)
eugene y
A: 
#!/usr/bin/perl

use Time::Local;

@dates = ('10/02/2004', '02/01/2004', '01/02/2004', '01/06/2004', 
          '01/02/2005', '01/12/2004');

print "@dates\n";

@ordered = sort { &compare } @dates;

sub compare {
$a =~ /(\d{2})\/(\d{2})\/(\d{4})/;
$amonth = $1;
$aday = $2;
$ayear = $3;

$b =~ /(\d{2})\/(\d{2})\/(\d{4})/;
$bmonth = $1;
$bday = $2;
$byear = $3;

$c = timelocal(0,0,0,$aday,$amonth,$ayear-1900);
$d = timelocal(0,0,0,$bday,$bmonth,$byear-1900);

$c <=> $d;
}

print "@ordered\n";

or:

@dates = ('10/02/2004', '02/01/2004', '01/02/2004', 
          '01/06/2004', '01/02/2005', '01/12/2004');

print "@dates\n";

@ordered = sort { &compare } @dates;

sub compare {
$a =~ /(\d{2})\/(\d{2})\/(\d{4})/;
$c = $3 . $1 . $2;

$b =~ /(\d{2})\/(\d{2})\/(\d{4})/;
$c = $3 . $1 . $2;

$c <=> $d;
}

print "@ordered\n";

This code does not belong to me. It's here courtesy of Google and a guy name dswimboy.

Marcel
It's missing a cached key sort. :)
brian d foy
+1  A: 

I prefer the YYYY/MM/DD format better, for just this reason. It's guaranteed to sort dates properly, between 1000/01/01 and 9999/12/31.

my @sorted_alt = sort map { join '/', reverse split '/', $_  } @dates;

If you really need it in DD/MM/YYYY format, you could always go for a complete Schwartzian transform.

my @sorted = map {
  join '/', reverse split '/', $_
}
sort
map {
  join '/', reverse split '/', $_ 
} @dates;

or

my @sorted = map {
  join '/', reverse @$_
}
sort { "@$a" cmp "@$b" }
map {
  [ reverse split '/', $_ ]
} @dates;
Brad Gilbert