在Perl中排序自定义日期

时间:2015-05-14 15:14:19

标签: perl

我有一个哈希,键是日期:

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月份之前。任何人都知道如何解决它?

4 个答案:

答案 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