Perl 根据更新的时间和严重性获取记录

Perl 根据更新的时间和严重性获取记录,perl,Perl,我有一些事件数据,我只需要根据事件的更新时间打印关于单个事件的单个数据 我将所有数据保存在数组的散列中,从中我需要从每个具有最新更新时间的事件中提取一条记录 以下是脚本: use strict; use warnings; my %data = ( '1342' => [ { 'Severity' => 'MEDIUM',

我有一些事件数据,我只需要根据事件的更新时间打印关于单个事件的单个数据

我将所有数据保存在数组的散列中,从中我需要从每个具有最新更新时间的事件中提取一条记录

以下是脚本:

use strict;
use warnings;

my %data = (
          '1342' => [
                      {
                        'Severity' => 'MEDIUM',
                        'Node' => 'Node002',
                        'State' => 'ACTIVE_UNACKNOWLEDGED',
                        'Updated' => '2020-10-01T12:00:00',
                        'eventId' => '1342'
                      },
                      {
                        'Severity' => 'HIGH',
                        'Node' => 'Node002',
                        'Updated' => '2020-10-01T12:05:00',
                        'eventId' => '1342',
                        'State' => 'ACTIVE_UNACKNOWLEDGED'
                      }
                    ],
          '1341' => [
                      {
                        'State' => 'ACTIVE_UNACKNOWLEDGED',
                        'eventId' => '1341',
                        'Updated' => '2020-10-01T12:10:00',
                        'Node' => 'Node001',
                        'Severity' => 'HIGH'
                      },
                      {
                        'State' => 'ACTIVE_UNACKNOWLEDGED',
                        'Updated' => '2020-10-01T12:15:00',
                        'eventId' => '1341',
                        'Severity' => 'HIGH',
                        'Node' => 'Node001'
                      },
                      {
                        'State' => 'CLEARED_ACKNOWLEDGED',
                        'Updated' => '2020-10-01T12:15:00',
                        'eventId' => '1341',
                        'Severity' => 'CLEARED',
                        'Node' => 'Node001'
                      }
                    ]
);

foreach my $id ( sort keys %data ){
    print "Event: $id\n";
    foreach my $record( sort { $b->{Updated} cmp $a->{Updated} } @{ $data{$id} }) {
        print "\t";
        print "$record->{Node},$record->{Severity},$record->{State},$record->{Updated}";
        print "\n";
        last;
    }
}
电流输出:

Event: 1341
        Node001,HIGH,ACTIVE_UNACKNOWLEDGED,2020-10-01T12:15:00
Event: 1342
        Node002,HIGH,ACTIVE_UNACKNOWLEDGED,2020-10-01T12:05:00
Event: 1341
        Node001,CLEARED,CLEARED_ACKNOWLEDGED,2020-10-01T12:15:00
Event: 1342
        Node002,HIGH,ACTIVE_UNACKNOWLEDGED,2020-10-01T12:05:00
预期输出:

Event: 1341
        Node001,HIGH,ACTIVE_UNACKNOWLEDGED,2020-10-01T12:15:00
Event: 1342
        Node002,HIGH,ACTIVE_UNACKNOWLEDGED,2020-10-01T12:05:00
Event: 1341
        Node001,CLEARED,CLEARED_ACKNOWLEDGED,2020-10-01T12:15:00
Event: 1342
        Node002,HIGH,ACTIVE_UNACKNOWLEDGED,2020-10-01T12:05:00
如果特定事件对于不同的严重性具有相同的更新时间,则应选择已清除严重性的记录

在上述情况下,我们可以看到对于
EventID
1341
清除的
高的
严重性同时存在,即
2020-10-01T12:15:00

因此,我应该选择
清除的
严重性记录,而不是
严重性记录。如何选择该记录?

迭代数据结构中的每个级别,以便能够根据需要进行比较

foreach my $ev (sort keys %data) { 
    my $ra = $data{$ev}; 
    
    my $fmt = "%Y-%m-%dT%T";

    # Initialize "best" time and its hashref
    my $hr = $ra->[0];
    my $dt = Time::Piece->strptime($hr->{Updated}, $fmt);
    
    foreach my $idx (1..$#$ra) { 
        my $dt_curr = Time::Piece->strptime(
            $ra->[$idx]{Updated}, $fmt
        );

        if ($dt_curr > $dt) {
            $dt = $dt_curr;
            $hr = $ra->[$idx];
        }
        elsif ($dt_curr == $dt) { 
            if ($ra->[$idx]{Severity} eq 'CLEARED') { 
                $dt = $dt_curr;
                $hr = $ra->[$idx];
            }
        }
    }   
    say "Event: $hr->{eventId}";
    say "\t", join ',', 
        map { $hr->{$_} } qw(Node Severity State Updated);
    # Or rather, as clarified in a comment
    #say join ',' 
    #    map { $hr->{$_} } qw(eventId Node Severity State Updated);
}    
使用提供的
%数据
,为了可读性而省略,这将打印预期结果。†

我假设数据结构中还没有更深层次的内容,所以我只复制hashref,否则它们应该被克隆(深度复制)

显示的时间戳(
%Y-%m-%dT%T
)可以直接按字典形式作为字符串进行比较(
$dt1 gt$dt2
),但我仍然使用了日期-时间模块,因为如果使用了不同的时间戳格式,它很容易调整

为了提高效率,可以对代码进行调整。(这也取决于数据细节。)


出现了一个问题:如果没有
Time::Piece
(或者我使用的任何库),如何实现它,因为它在该安装中不可用。关于如何使用这种(残废的)工具,请看我的评论,但这里有一个简单的方法。(这也更快。)

分隔符的
split
参数是一个正则表达式。当字符类
[-T://code>中的字符在字符串中匹配(按给定顺序尝试)时,字符串将在其上拆分。它们的顺序与时间戳相同,因此
split
返回:年、月、日、小时、分钟、秒

有了这样一个每个时间戳的列表,我们可以通过按顺序比较组件(比较年份,然后是月份,…)来清楚地比较它们的日期时间

但是,使用
Time::Local
(已确认安装),您可以通过使用
timelocal
获取自epoch以来的秒数并进行比较,从而避免编写冗长(且容易出错)的代码来比较组件

use Time::Local;
# parse timestamps to get yr, mon, ... for each
my $dt1 = timelocal($sec1, $min1, $hour1, $mday1, $mon1, $yr1);
my $dt2 = ...

if ($dt1 > $dt2) { ... }
总之,通过一次基本的健康检查

sub ts_to_epoch {
    my ($timestamp) = @_;

    # For format year-mon-dayThour:minute:second
    my @dt_parts = split /[-T:]/, $timestamp;
    die "Parsing of '$timestamp' failed" if @dt_parts != 6;

    return timelocal( reverse @dt_parts );
}

my $epoch_1 = ts_to_epoch($timestamp1);
...
如果您需要从epoch之后的几秒钟中检索时间戳,那么使用有限的工具集的一种方法是使用
localtime($epoch)


†最后一行代码(注释掉)按所需顺序打印,按该顺序列出键。但如果这出现在代码中的许多地方,那么我们就会有分散的硬编码密钥列表;当添加或重命名另一个键时会发生什么?我们必须搜捕所有这些地方

相反,在一个辅助散列中拼写出顺序

# One place in code where the hard-coded order is given
my %sort_by = do {
    my @keys = qw(eventId Node Severity State Updated);
    map { $keys[$_] => $_ } 0..$#keys;
};  
现在通过使用
获取键,然后对它们进行排序来打印

say join ',', 
    map { $hr->{$_} } 
    sort { $sort_by{$a} <=> $sort_by{$b} } 
    keys %$hr;
说连接',',
映射{$hr->{$}
排序{$sort_by{$a}$sort_by{$b}
密钥%$hr;

因此,
%sort\u by
在代码中的一个位置给出(并保持不变!),并且打印语句不使用任何特定的硬编码数据。

OP代码中的一个小更改会产生所需的结果

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

use Data::Dumper;

my %data = (
          '1342' => [
                      {
                        'Severity' => 'MEDIUM',
                        'Node' => 'Node002',
                        'State' => 'ACTIVE_UNACKNOWLEDGED',
                        'Updated' => '2020-10-01T12:00:00',
                        'eventId' => '1342'
                      },
                      {
                        'Severity' => 'HIGH',
                        'Node' => 'Node002',
                        'Updated' => '2020-10-01T12:05:00',
                        'eventId' => '1342',
                        'State' => 'ACTIVE_UNACKNOWLEDGED'
                      }
                    ],
          '1341' => [
                      {
                        'State' => 'ACTIVE_UNACKNOWLEDGED',
                        'eventId' => '1341',
                        'Updated' => '2020-10-01T12:10:00',
                        'Node' => 'Node001',
                        'Severity' => 'HIGH'
                      },
                      {
                        'State' => 'ACTIVE_UNACKNOWLEDGED',
                        'Updated' => '2020-10-01T12:15:00',
                        'eventId' => '1341',
                        'Severity' => 'HIGH',
                        'Node' => 'Node001'
                      },
                      {
                        'State' => 'CLEARED_ACKNOWLEDGED',
                        'Updated' => '2020-10-01T12:15:00',
                        'eventId' => '1341',
                        'Severity' => 'CLEARED',
                        'Node' => 'Node001'
                      }
                    ]
);

foreach my $id ( sort keys %data ){
    print "Event: $id\n";
    my $found;
    for( @{$data{$id}} ) {
        $found = $_ unless defined $found;
        if( $_->{Updated} eq $found->{Updated} ) {
            $found = $_ if $_->{Severity} eq 'CLEARED';
        }
        $found = $_ if $_->{Updated} ge $found->{Updated};
    }
    say "\t" . join ',', $found->@{qw/Node Severity State Updated/};
}
输出

Event: 1341
        Node001,CLEARED,CLEARED_ACKNOWLEDGED,2020-10-01T12:15:00
Event: 1342
        Node002,HIGH,ACTIVE_UNACKNOWLEDGED,2020-10-01T12:05:00

这将产生预期的结果。在这里,我使用的是
perl5.10
,安装量很小,没有核心模块。Perl 5.10最小安装附带的
Time::Piece
Perl模块是否有任何可能性/替代方案。@vkk05如果不能使用库,则必须编写代码来解析日期时间字符串,并逐段进行比较。(正如我在回答中所指出的,在这个问题中,使用时间戳,您可以按字典顺序将它们作为字符串进行比较。但是通过注释中的问题,我假设格式确实不一定如图所示,并且您希望将其视为datetime。如果您不能使用ready库,那么您必须为此编写自己的代码。)@vkk05甚至不能使用核心模块,这是一个有点不可能的困境。主要计算环境中的工具是结构化和组织的,良好的功能块是通过设计(通常是必要的)在模块/库中实现的。如果你不能使用这些工具,那么你就是在使用一个残废的工具,你必须自己实现一些功能。这可能没问题(通常情况下,当您在一个已编译的脚本语言中寻求方便的时候!),比较两个日期很简单。但是,祝你在没有图书馆的情况下上网或使用数据库时好运。@vkk05我不是要批评你或诸如此类的人,只是说如果你使用一种语言的有限工具集,你应该会遇到一些困难。相关:谢谢@Polar Bear。逻辑看起来简单而有趣,我得到了预期的输出。但是使用
ge
操作符进行时间比较可以吗?@vkk05您的ISO时间戳具有非常有趣的特性,可以按顺序进行排序。
YYYY-MM-DD
格式远远优于本地化格式,如
DD.MM.YYYY
MM/DD/YYYY
,因为它基本上是以正确的顺序内置的,就像在查看以日期命名的文件夹时一样。
T
总是相同的,并且
HH:MM:SS
字符串总是按照预期的方式排序。是的,你可以用一个更大的字符串来表示。无需解析。@vkk05——在这种特殊情况下,使用的截止日期格式
ge
运算符是适用的。如果日期格式不同,那么您必须对其进行一些操作,使其可排序,而无需借助某些模块进行解析