2016-11-29 113 views
1

以下是我的代码:如何使用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"; 

预期输出应该按升序排列的日期。

+1

请检查答案和投票,如果你接受相同的...! – ssr1012

回答

0

请尝试以下琐碎的排序功能。
警告:不传导数据格式检查任何

sub sortByDate{ 
    my @a = split(/\//,$a); 
    my @b = split(/\//,$b); 
    return $a[2]<=>$b[2] || $a[0]<=>$b[0] || $a[1]<=>$b[1]; 
} 
0

您是否着眼于可用的日期操作模块?我敢肯定,有一些将为你做大部分工作...

无论如何,你的代码的答案上面:你的排序子有很多问题;该代码甚至不会在Perl 5.20上运行:

  1. 斜线不会转义,但它们是模式分隔符。 我使用m## - 在OP中也修正了Escape。
  2. 您没有重新排列日期组件。总的来说,首先要把年份缩短;你认为你在做什么? I sprintf()'将该日期编成固定的YYYYMMDD号码。
  3. 你只是指定从比赛的第一个元素在$adate$bdate,即只有一个月排序 - 使用sprintf()
  4. 你匹配的固定日期组件字符串,但在你的例子日/月有一方固定或两个字符。相反,我使用\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; 
} 
4

你所追求的:

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

-1

可能有多种方法来使用Perl sort日期。我在脚本中总结了两种方法。


这两种方法都使用Schwartzian transform对它进行排序。

  1. 只需使用拆分,然后根据年,月和日进行排序。使用它,如果你确定输入格式。
  2. 使用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; 
2

转换为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中导致警告,因为您不再按数字顺序排序。

相关问题