简体   繁体   English

如何使用Perl按升序对日期排序?

[英]How do I sort the date in ascending order using Perl?

The following is my code: 以下是我的代码:

@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";

The expected output should be the date in ascending order. 预期输出应为升序排列的日期。

You were going for: 您打算去:

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
}

If you were to rearrange the date into the form yyyymmdd , you could simply use a lexicographical sort. 如果要将日期重新排列为yyyymmdd ,则可以简单地按字典顺序使用。

I believe this is the fastest solution: 我相信这是最快的解决方案:

my @sorted_events =
    map { substr($_, 8) }
    sort
    map { sprintf('%3$04d%1$02d%2$02d%4$s', split(qr{/}, $_), $_) }
    @events;

Convert to Time::Piece first, then sort works exactly like you'd expect. 首先转换为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;

Note - in the above, if strptime fails, it stays the original string. 注意-在上面,如果strptime失败,它将保留原始字符串。 This will cause a warning in sort because you're not sorting numerically any more. 这将导致sort警告,因为您不再对数字进行分类。

Try the following trivial sort function. 尝试以下简单的排序功能。
WARNING: It conducts no data format check whatsoever 警告:不会进行任何数据格式检查

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

Have you looked at the date manipulation modules available? 您是否看过可用的日期处理模块? I'm sure there's some that will do most of the work for you... 我敢肯定,有些可以为您完成大部分工作...

Anyway the answer to your code above: your sort sub has many issues; 无论如何,上面代码的答案是:您的sort子有很多问题; the code doesn't even run on Perl 5.20: 该代码甚至无法在Perl 5.20上运行:

  1. The slashes aren't escaped, but they're the pattern delimiter. 斜杠不能转义,但它们是模式定界符。 I used m## -- Escapes fixed in OP too. 我使用了m## -在OP中也修复了转义符。
  2. You're not re-ordering the date components. 您不需要重新排序日期组件。 The whole point is to short the year first; 重点是首先缩短一年; where do you think you're doing it? 您认为您在哪里做? I sprintf() 'ed the date into a fixed YYYYMMDD number. sprintf()将日期转换成固定的YYYYMMDD数字。
  3. You're only assigning the first element from the matches in $adate and $bdate , ie sorting only the month -- fixed using sprintf() 您只分配了$adate$bdate的匹配项中的第一个元素,即仅排序月份-使用sprintf()修复
  4. You match fixed strings in date components, but day/month in your example has either one or two characters. 您在日期组件中匹配了固定的字符串,但是示例中的天/月具有一个或两个字符。 I used \\d{1,2} for those instead. 我改用\\d{1,2}

Here's a fixed sub: 这是一个固定的子:

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;
}

There is still no error checking, so running with strict/warning will return a lot of errors if you pass invalid data. 仍然没有错误检查,因此如果您传递无效数据,则使用严格/警告运行将返回很多错误。 You don't need the extra code if you validate the format first, but to prevent errors from badly formed dates you can also add some checking and use string cmp fallback: 如果首先验证格式,则不需要额外的代码,但是为防止格式错误的日期产生错误,您还可以添加一些检查并使用字符串cmp fallback:

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;
}

There could be multiple approaches to sort dates using Perl. 使用Perl可能有多种方法sort日期进行sort I have summarized two approaches in the script. 我在脚本中总结了两种方法。


Both the approach uses Schwartzian transform to sort it. 两种方法都使用Schwartzian变换对其进行排序。

  1. Simply using split and then sort it based on year, month and day. 只需使用split,然后根据年,月和日对其进行排序。 Use it, if you are sure about the input format. 如果您确定输入格式,请使用它。
  2. Using Time::Piece to parse into Time::Piece object and then sort it. 使用Time::Piece解析为Time::Piece对象,然后对其进行排序。 Use it, if you are not sure about input format and need validation before processing. 如果不确定输入格式并且需要在处理之前进行验证,请使用它。

You can use anyone between them based on your preferences. 您可以根据自己的喜好在他们之间使用任何人。

#!/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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM