2016-11-29 148 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中導致警告,因爲您不再按數字順序排序。

相關問題