我有一个哈希,键是日期:
my %dates = (
'May 13, 2015' => 8,
'May 7, 2015' => 1,
'Apr 29, 2015' => 2,
'May 12, 2015' => 1,
'Apr 16, 2015' => 13,
'May 6, 2015' => 1,
);
我试图按日期对它们进行排序。我尝试了两种方法:
foreach my $k (sort {join('', (split ' ', $a)[2,0,1]) <=> join('', (split ' ', $a)[2,0,1])} keys(%dates))
{ print $k . " = " . $dates{$k}; }
由于月份是一个字符串而无法正常工作,而且:
foreach my $k (sort {join('', (split ' ', $a)[2,0,1]) cmp join('', (split ' ', $a)[2,0,1])} keys(%dates))
{ print $k . " = " . $dates{$k}; }
也不起作用,只是将4月份全部放在5月份之前。任何人都知道如何解决它?
答案 0 :(得分:5)
您可以使用Time::Piece
解析日期#!/usr/bin/env perl
use strict;
use warnings;
use Time::Piece;
my %dates = (
'May 13, 2015' => 8,
'May 7, 2015' => 1,
'Apr 29, 2015' => 2,
'May 12, 2015' => 1,
'Apr 16, 2015' => 13,
'May 6, 2015' => 1,
);
for my $k (sort by_date keys %dates) {
print "$k => $dates{$k}\n";
}
sub by_date {
my ($ta, $tb) = map Time::Piece->strptime($_, '%b %d, %Y'), $a, $b;
$ta <=> $tb;
}
输出:
Apr 16, 2015 => 13 Apr 29, 2015 => 2 May 6, 2015 => 1 May 7, 2015 => 1 May 12, 2015 => 1 May 13, 2015 => 8
当然,如果你想放弃Time::Piece的好处,你可以按照以下方针做点什么:
#!/usr/bin/env perl
use 5.010;
use strict;
use warnings;
my %dates = (
'May 13, 2015' => 8,
'May 7, 2015' => 1,
'Apr 29, 2015' => 2,
'May 12, 2015' => 1,
'Apr 16, 2015' => 13,
'May 6, 2015' => 1,
);
for my $k (sort by_ugliness keys %dates) {
print "$k => $dates{$k}\n";
}
sub by_ugliness {
state $months = {
do {
my $i = 1;
map {$_ => $i++}
qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
}
};
my ($ta, $tb) = map [
/\A (\S+) \s+ ([0-9]{1,2}), \s+ ([0-9]{4})\z/x
], $a, $b;
($ta->[2] <=> $tb->[2]) ||
($months->{ $ta->[0] } <=> $months->{ $tb->[0]}) ||
($ta->[1] <=> $tb->[1]) ;
}
在某些时候,您将开始改进该模式匹配。不要; - )
答案 1 :(得分:2)
你认为日期是什么,不是:它们是字符串。你不能做你想做的事情。首先,您必须将它们变成日期。
Perl以Time::Piece
的形式提供了一个很好的模块。
my $date = Time::Piece -> strptime ( 'May 13, 2015', '%b %d, %Y' );
print $date;
所以要做你想要的排序:
my %dates = (
'May 13, 2015' => 8,
'May 7, 2015' => 1,
'Apr 29, 2015' => 2,
'May 12, 2015' => 1,
'Apr 16, 2015' => 13,
'May 6, 2015' => 1,
);
foreach my $key (
sort {
Time::Piece->strptime( $a, '%b %d, %Y' )
<=> Time::Piece->strptime( $b, '%b %d, %Y' )
} keys %dates
)
{
print "$key, $dates{$key}\n";
}
答案 2 :(得分:2)
我准确发布了SinanÜnür写的内容,所以这里有一个替代解决方案,以防任何人使用它。
这使用自定义Schwartzian Transform来比较三个日期字段中的每一个,并使用将月份名称转换为数字的哈希%months
use strict;
use warnings;
use 5.010;
my %dates = (
'May 13, 2015' => 8,
'May 7, 2015' => 1,
'Apr 29, 2015' => 2,
'May 12, 2015' => 1,
'Apr 16, 2015' => 13,
'May 6, 2015' => 1,
);
my %months = do {
my $m = 0;
map { $_ => ++$m } qw/ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec /;
};
my @sorted =
map { $_->[0] }
sort {
$a->[3] <=> $b->[3] or
$months{$a->[1]} <=> $months{$b->[1]} or
$a->[2] <=> $b->[2]
}
map { [ $_, /\w+/g ] } keys %dates;
say for @sorted;
<强>输出强>
Apr 16, 2015
Apr 29, 2015
May 6, 2015
May 7, 2015
May 12, 2015
May 13, 2015
答案 3 :(得分:0)
这是一个不需要额外模块的替代方案:
#! /usr/bin/env perl
use strict;
use warnings;
use 5.010;
my %dates = (
'May 13, 2015' => 8,
'May 7, 2015' => 1,
'Apr 29, 2015' => 2,
'May 12, 2015' => 1,
'Apr 16, 2015' => 13,
'May 6, 2015' => 1,
);
my $i = 1;
my %month_map = map { $_ => $i++ } qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
my @dates = map { "$_->[0] => $dates{$_->[0]}" }
sort { $a->[1] <=> $b->[1] }
map { [$_, date_to_number($_) ]} keys %dates;
for my $d ( @dates ) {
say $d;
}
sub date_to_number {
my $date_str = shift;
if ( $date_str =~ m/^([^\s]{3})\s([^,]+),\s(\d{4})$/ ) {
return $3 . ${\(length($month_map{$1}) == 1 ? '0' . $month_map{$1} : $month_map{$1})} . ${\(length($2) == 1 ? '0' . $2 : $2)};
}
return 0;
}
输出将执行以下操作:
Apr 16, 2015 => 13
Apr 29, 2015 => 2
May 6, 2015 => 1
May 7, 2015 => 1
May 12, 2015 => 1
May 13, 2015 => 8