The following is my code:
@events=('11/17/1999', '12/6/1999', '12/23/1999',
' 1/23/2000', '1/13/2000', '2/25/2000',
'1/5/2000', '3/18/2000', '4/10/2000',
'3/12/2000', '12/31/1999');
sub sortByDate{
$adate=$a=~ /(\d{2})\/(\d{2})\/(\d{4})/;
$bdate=$b=~ /(\d{2})\/(\d{2})\/(\d{4})/;
$adate <=> $bdate;
}
@ascending = sort sortByDate @events;
print "@ascending\n";
The expected output should be the date in ascending order.
You were going for:
sub sortByDate{
my ($am, $ad, $ay) = $a =~ /(\d{2})\/(\d{2})\/(\d{4})/;
my ($bm, $bd, $by) = $b =~ /(\d{2})\/(\d{2})\/(\d{4})/;
$ay <=> $ay || $am <=> $bm || $ad <=> $bd
}
If you were to rearrange the date into the form yyyymmdd
, you could simply use a lexicographical sort.
I believe this is the fastest solution:
my @sorted_events =
map { substr($_, 8) }
sort
map { sprintf('%3$04d%1$02d%2$02d%4$s', split(qr{/}, $_), $_) }
@events;
Convert to Time::Piece
first, then sort
works exactly like you'd expect.
#!/usr/bin/perl
use strict;
use warnings;
use Time::Piece;
my @events=('11/17/1999', '12/6/1999', '12/23/1999',
'1/23/2000', '1/13/2000', '2/25/2000',
'1/5/2000', '3/18/2000', '4/10/2000',
'3/12/2000', '12/31/1999');
foreach my $event ( @events ) {
$event = eval { Time::Piece->strptime($event, "%m/%d/%Y" )} || $event;
}
print join "\n", sort { $a <=> $b } @events;
print "\n";
print join "\n", map { $_ -> strftime("%Y-%m-%d") } sort { $a <=> $b } @events;
Note - in the above, if strptime
fails, it stays the original string. This will cause a warning in sort
because you're not sorting numerically any more.
Try the following trivial sort function.
WARNING: It conducts no data format check whatsoever
sub sortByDate{
my @a = split(/\//,$a);
my @b = split(/\//,$b);
return $a[2]<=>$b[2] || $a[0]<=>$b[0] || $a[1]<=>$b[1];
}
Have you looked at the date manipulation modules available? I'm sure there's some that will do most of the work for you...
Anyway the answer to your code above: your sort sub has many issues; the code doesn't even run on Perl 5.20:
m##
-- Escapes fixed in OP too. sprintf()
'ed the date into a fixed YYYYMMDD
number. $adate
and $bdate
, ie sorting only the month -- fixed using sprintf()
\\d{1,2}
for those instead. Here's a fixed sub:
sub sortByDate {
$a =~ m#(\d{1,2})/(\d{1,2})/(\d{4})#;
my $adate = sprintf('%i%02i%02i', $3, $1, $2);
$b =~ m#(\d{1,2})/(\d{1,2})/(\d{4})#;
my $bdate = sprintf('%i%02i%02i', $3, $1, $2);
return $adate <=> $bdate;
}
There is still no error checking, so running with strict/warning will return a lot of errors if you pass invalid data. You don't need the extra code if you validate the format first, but to prevent errors from badly formed dates you can also add some checking and use string cmp fallback:
sub sortByDate {
my $adate = sprintf('%i%02i%02i', $3, $1, $2)
if ($a =~ m#(\d{1,2})/(\d{1,2})/(\d{4})#);
my $bdate = sprintf('%i%02i%02i', $3, $1, $2)
if ($b =~ m#(\d{1,2})/(\d{1,2})/(\d{4})#);
return $adate <=> $bdate if ($adate && $bdate);
return $a cmp $b;
}
There could be multiple approaches to sort
dates using Perl. I have summarized two approaches in the script.
Both the approach uses Schwartzian transform to sort it.
Time::Piece
to parse into Time::Piece
object and then sort it. Use it, if you are not sure about input format and need validation before processing. You can use anyone between them based on your preferences.
#!/usr/bin/perl
use strict;
use warnings;
use Time::Piece;
my @events = ( '11/17/1999', '12/6/1999', '12/23/1999',
'1/23/2000', '1/13/2000', '2/25/2000',
'1/5/2000', '3/18/2000', '4/10/2000',
'3/12/2000', '12/31/1999');
my @sorted_events = map { $_ -> [0] }
sort {
# year
$a -> [1] -> [2] <=> $b -> [1] -> [2] ||
# month
$a -> [1] -> [0] <=> $b -> [1] -> [0] ||
# day
$a -> [1] -> [1] <=> $b -> [1] -> [1]
}
map { [ $_, [ split /\// ] ] }
@events;
# you can also use Time::Piece
my @sorted_events_again = map { $_ -> [1] }
sort { $a -> [0] <=> $b -> [0] }
map { [ Time::Piece -> strptime($_, "%m/%d/%Y"), $_ ] }
@events;
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.