如何使用Perl按升序对日期进行排序?
问题描述:
以下是我的代码:如何使用Perl按升序对日期进行排序?
@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";
预期输出应该按升序排列的日期。
答
请尝试以下琐碎的排序功能。
警告:不传导数据格式检查任何
sub sortByDate{
my @a = split(/\//,$a);
my @b = split(/\//,$b);
return $a[2]<=>$b[2] || $a[0]<=>$b[0] || $a[1]<=>$b[1];
}
答
您是否着眼于可用的日期操作模块?我敢肯定,有一些将为你做大部分工作...
无论如何,你的代码的答案上面:你的排序子有很多问题;该代码甚至不会在Perl 5.20上运行:
-
斜线不会转义,但它们是模式分隔符。我使用m##
- 在OP中也修正了Escape。 - 您没有重新排列日期组件。总的来说,首先要把年份缩短;你认为你在做什么? I
sprintf()
'将该日期编成固定的YYYYMMDD
号码。 - 你只是指定从比赛的第一个元素在
$adate
和$bdate
,即只有一个月排序 - 使用sprintf()
- 你匹配的固定日期组件字符串,但在你的例子日/月有一方固定或两个字符。相反,我使用
\d{1,2}
。
这里有一个固定的子:
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;
}
目前仍没有错误检查,以严格的/警告使运行如果传递无效数据将返回错误很多。如果先验证格式,你不需要额外的代码,但要防止错误严重形成的约会,你还可以添加一些检查和使用字符串CMP回退:
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;
}
答
你所追求的:
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
}
如果你要重新安排日期到表单yyyymmdd
,你可以简单地使用的逐一排序。
我相信这是最快的解决方案:
my @sorted_events =
map { substr($_, 8) }
sort
map { sprintf('%3$04d%1$02d%2$02d%4$s', split(qr{/}, $_), $_) }
@events;
+1
真棒回答。 – ssr1012
答
可能有多种方法来使用Perl sort
日期。我在脚本中总结了两种方法。
这两种方法都使用Schwartzian transform对它进行排序。
- 只需使用拆分,然后根据年,月和日进行排序。使用它,如果你确定输入格式。
- 使用
Time::Piece
解析为Time::Piece
对象,然后对其进行排序。如果您不确定输入格式并在处理前需要验证,请使用它。
根据您的喜好,您可以使用它们之间的任何人。
#!/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;
答
转换为Time::Piece
,再sort
作品完全一样,你会期望。
#!/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;
注 - 在上面,如果strptime
失败,它保持原始字符串。这会在sort
中导致警告,因为您不再按数字顺序排序。
请检查答案和投票,如果你接受相同的...! – ssr1012