J'ai des données d'événement, je n'ai besoin d'imprimer qu'une seule donnée par rapport à un seul événement en fonction de l'heure mise à jour de l'événement.

J'ai toutes les données enregistrées dans le hachage des tableaux et à partir de là, je dois extraire un seul enregistrement de chacun des événements qui ont la dernière mise à jour.

Voici le script:

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

Sortie actuelle:

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

Résultat attendu:

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

Si l'événement particulier a la même heure de mise à jour pour différentes gravités, il doit sélectionner l'enregistrement dont la gravité est EFFACÉE.

Dans le cas ci-dessus, nous pouvons voir pour EventID 1341 qu'il y a CLEARED et HIGH gravité existe en même temps, c'est-à-dire 2020-10-01T12:15:00. Donc, je devrais choisir l'enregistrement de gravité CLEARED au lieu de HIGH gravité. Comment puis-je choisir cet enregistrement?

3
vkk05 4 oct. 2020 à 11:27

2 réponses

Meilleure réponse

Itérez sur chaque niveau de votre structure de données afin de pouvoir comparer au besoin

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

Avec le %data fourni, omis pour la lisibilité, cela imprime les résultats attendus.

Je suppose qu'il n'y a pas encore de niveaux plus profonds dans la structure des données et donc je copie simplement les hashrefs, sinon ils devraient être clonés (copiés en profondeur).

Les horodatages affichés (%Y-%m-%dT%T) peuvent être directement comparés lexicographiquement sous forme de chaînes ($dt1 gt $dt2) mais j'ai toujours utilisé un module date-heure car il est alors facile d'ajuster si un format d'horodatage différent finit par être utilisé.

Le code peut être modifié pour plus d'efficacité. (Cela dépend également des détails des données.)


Une question s'est posée sur la façon de le faire sans Time::Piece (ou toute bibliothèque que je prends), car il n'est pas disponible dans cette installation. S'il vous plaît voir les commentaires pour ce que j'aimerais dire sur le travail avec un tel outil (paralysé), mais voici un moyen simple. (C'est aussi plus rapide.)

sub parse_ts {               # add checks
    my ($timestamp) = @_;
    
    # For format year-mon-dayThour:minute:second
    return split /[-T:]/, $timestamp;
}

my ($yr, $mon, $day, $hr, $min, $sec) = parse_ts($timestamp);

L'argument de split pour le séparateur est une expression régulière. Comme un caractère de la classe de caractères [-T:] est mis en correspondance dans la chaîne (essayé dans l'ordre donné), la chaîne est divisée dessus. Leur ordre est comme dans l'horodatage donc split renvoie: année, mois, jour, heure, minute, seconde.

Avec une telle liste pour chaque horodatage, nous pouvons comparer intelligemment leurs dates-heures, en comparant les composants dans cet ordre (comparer l'année, puis le mois, ...).

Cependant, avec Time::Local (dont l'installation est confirmée), vous pouvez éviter d'écrire le code fastidieux (et sujet aux erreurs) pour comparer les composants, en obtenant les secondes depuis l'époque en utilisant timelocal et en comparant cela

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) { ... }

Au total, avec un contrôle de santé rudimentaire

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);
...

Si jamais vous avez besoin de récupérer un horodatage de ces secondes depuis l'époque, un moyen avec votre ensemble d'outils limité est avec localtime($epoch).


Au lieu de cela, énoncez l'ordre dans un hachage auxiliaire

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

Et maintenant imprimez en récupérant les clés en utilisant keys puis en les triant

say join ',', 
    map { $hr->{$_} } 
    sort { $sort_by{$a} <=> $sort_by{$b} } 
    keys %$hr;

Ainsi %sort_by est donné à un endroit dans le code (et le rend constant!), Et les instructions d'impression n'utilisent aucune donnée spécifique codée en dur.

4
zdim 10 oct. 2020 à 04:34

Un petit changement dans le code de l'OP produit le résultat souhaité

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

Production

Event: 1341
        Node001,CLEARED,CLEARED_ACKNOWLEDGED,2020-10-01T12:15:00
Event: 1342
        Node002,HIGH,ACTIVE_UNACKNOWLEDGED,2020-10-01T12:05:00
1
Polar Bear 4 oct. 2020 à 18:46