perl对日期时间字符串的多个arryref进行排序

时间:2018-11-26 13:07:14

标签: perl sorting arrayref

我想对arrayref%results(时间字符串,从旧到新)进行排序,它具有多个键,但我只是张贴了一个键以显示它的外观:

'Ende Monatswechsel P-Konten' => [
                                         '17.02.2018 05:17:39',
                                         '14.02.2018 04:28:11',
                                         '23.02.2018 03:17:17',
                                         '22.02.2018 03:39:20',
                                  ]

我期望:

    'Ende Monatswechsel P-Konten' => [
                                         '14.02.2018 04:28:11',
                                         '17.02.2018 05:17:39',
                                         '22.02.2018 03:39:20',
                                         '23.02.2018 03:17:17',
                                  ]

有人知道该怎么做吗? 我尝试过:

my $columns = map [ $_, sort{$a <=> $b} @{ $results{$_} } ], keys %results;

但是它不起作用。 预先感谢。

我的代码如下:

while(my $line=<F>) {
    #- Info: 19.02.2018 00:01:01 --- Start Tageswechsel-CoBa ---
    #- Info: 27.11.2018 04:16:42 --- Ende Tageswechsel-CoBa ---
            if ($line=~ /(\d\d\.\d\d\.\d\d\d\d \d\d:\d\d:\d\d) --- (.+? Tageswechsel-CoBa) -.*\s*$/)
            {
                    ($timestamp, $action) = ($1,$2);
            }
            if ( !defined $filter{$action}{$timestamp} ) {
                    push @{$results{$action}}, $timestamp;
                    $filter{$action}{$timestamp} = 1;
            }
}

print Dumper(\%results)输出:

'Start Tageswechsel-CoBa' => [
                                '17.02.2018 05:12:13',
                                '20.02.2018 04:23:16',
                                '22.02.2018 03:12:46',
                                '23.02.2018 03:34:28',
                                '27.02.2018 03:41:25',
                                '02.03.2018 03:32:26',
            ],
'Ende Tageswechsel-CoBa' => [
                                    '17.02.2018 05:20:01',
                                    '19.02.2018 06:01:02',
                                    '20.02.2018 04:29:44',
                                    '22.02.2018 03:19:04',
                                    '23.02.2018 03:40:52',
                                    '26.02.2018 06:01:26',
            ]
            };

3 个答案:

答案 0 :(得分:2)

类似的事情会起作用:

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my $data = [
  '17.02.2018 05:17:39',
  '14.02.2018 04:28:11',
  '23.02.2018 03:17:17',
  '22.02.2018 03:39:20',
];

my @sorted = sort {
  my @a = split /[\. ]/, $a;
  my @b = split /[\. ]/, $b;
  return (
    $a[2] <=> $b[2] or  # year
    $a[1] <=> $b[1] or  # month
    $a[0] <=> $b[0] or  # day of month
    $a[3] cmp $b[3]     # time
  );
} @$data;

say Dumper @sorted;

我将每个值分成多个块,然后将它们从最大的块排序到最小的块。请注意,因为时间是字符串,而不是数字,所以我使用cmp而不是<=>

这有点低效,因为我要重新分割每个数据项几次。如果这是一个问题,那么您可以考虑使用Schwartzian变换之类的东西。

但是最好的解决方案是首先获得可排序的时间戳。如果您的日期是YYYY.MM.DD HH:MM:SS,那么您可以进行简单的字符串排序。

更新:我的输出是

$ perl sortdate
$VAR1 = '14.02.2018 04:28:11';
$VAR2 = '17.02.2018 05:17:39';
$VAR3 = '22.02.2018 03:39:20';
$VAR4 = '23.02.2018 03:17:17';

更新2:我已经编辑了代码,使其更像您的示例。希望这会有所帮助。

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my %results = (
  'Ende Monatswechsel P-Konten' => [
    '17.02.2018 05:17:39',
    '14.02.2018 04:28:11',
    '23.02.2018 03:17:17',
    '22.02.2018 03:39:20',
  ]
);

foreach my $k (keys %results) {
  my @sorted = sort {
    my @a = split /[\. ]/, $a;
    my @b = split /[\. ]/, $b;
    return (
      $a[2] <=> $b[2] or  # year
      $a[1] <=> $b[1] or  # month
      $a[0] <=> $b[0] or  # day of month
      $a[3] <=> $b[3]     # time
    );
  } @{ $results{$k} };

  $results{$k} = \@sorted;
}

say Dumper \%results;

还有输出...

$VAR1 = {
          'Ende Monatswechsel P-Konten' => [
                                             '14.02.2018 04:28:11',
                                             '17.02.2018 05:17:39',
                                             '22.02.2018 03:39:20',
                                             '23.02.2018 03:17:17'
                                           ]
        };

答案 1 :(得分:1)

分割字符串并比较部分适合于对许多类型的“多部分”值进行排序,但是,由于要处理日期时间,因此可以使用核心模块Time::Piece将字符串转换为日期时间对象,该对象可以使用<=>运算符进行比较。

Time::Piece提供了strptime方法,该方法使用格式字符串将日期字符串解析为Time::Piece对象。可以使用数值比较运算符比较Time::Piece个对象。

use v5.10;
use strict
use warnings;
use Time::Piece;

my @vals = (
    '17.02.2018 05:17:39',
    '14.02.2018 04:28:11',
    '23.02.2018 03:17:17',
    '22.02.2018 03:39:20',
);

say for sort {dt($a) <=> dt($b)} @vals;

###

sub dt {
    my $str = shift;
    return Time::Piece->strptime($str,'%e.%m.%Y %H:%M:%S') 
}

答案 2 :(得分:0)

我现在实际上使用了Dave的方法(因为我没有安装模块Time :: Piece),但略有不同,但是现在可以使用了,虽然不确定效率:

my @array;
my @sorted;
my %aref_n;

for my $key ( keys %results ) {
    for my $i (0..$#{ $results{$key} }) {
            push @array, $results{$key}[$i];
    }

    @sorted = sort {
            my @a = split /[\. ]/, $a;
            my @b = split /[\. ]/, $b;
            return (
                    $a[2] <=> $b[2] or
                    $a[1] <=> $b[1] or
                    $a[0] <=> $b[0] or
                    $a[3] cmp $b[3]
                    );
            } @array;

    $aref_n{$key} = [ @sorted ];
    @array=();

}