techworkpc has asked for the wisdom of the Perl Monks concerning the following question:

Hello Monks,

I'm new to Perl and got into a problem which I need your help to solve. I have a hash like below (using Dumper)

print '%ERROR1: ' .Dumper \%ERROR1; 'Oracle' => { 'server1' => { 'BACKUP ALERT' => { 'D +AILY' => { + '1433471420' => 1 + } }, 'FILESYSTEM ALERT' => { 'WARNING 8 +5% FULL' => { + '1433523904' => 1, + '1433520305' => 1, + '1433451904' => 1, + '1433450109' => 1, + '1433455504' => 1, + '1433509506' => 1, + '1433529308' => 1, + '1433534713' => 1 + } + }, }, 'server2' => { 'FILESYSTEM ALERT' => { +'WARNING 85% FULL' => { + '1433523635' => 1, + '1433455225' => 1, + '1433498427' => 1, + '1433512846' => 1, + '1433534424' => 1 + } }, + .. ... ..

This is a big hash with thousands of records, this has all the server alerts and diferrent timestamps that occurred. The 4th element is the timestamp.

I want to capture the last timestamp value occurrence for each server's generated alerts. something that should look like below

group Hostname Service Count State + Alert Started Alert Duration Oracle server1 BACKUP ALERT 1 DAILY + 1433471420 10 MINUTES Oracle server1 FILESYSTEM ALERT 8 WARNING 85% FULL + 1433523904 1 HOUR Oracle server2 .. ..

I'm able to capture the group, hostname, service, count and state from the hash using map (as follows) but do not know how to capture the first and last timestamp values. I've defined $ts1 and $ts2 for the same.

my @LIST = map { [ $_->[0], $_->[1], $_->[2], $_->[3], $_->[4], $_->[5],$_->[6] + ] } sort { $a->[0] cmp $b->[0] || $b->[4] <=> $a->[4] || $a->[1] cmp $b-> +[1] || $a->[2] cmp $b->[2] || $a->[3] cmp $b->[3] } map { my $group = $_; map { my $host = $_; map { my $service = $_; map { my $state = $_; my $count = keys %{$ERROR1{$group}->{$host}->{ +$service}->{$state}}; my $ts1 = (sort (keys %{$ERROR1{$group}->{$hos +t}->{$service}->{$state}}))[0]; my $ts2 = (sort(keys %{$ERROR1{$group}->{$host +}->{$service}->{$state}}))[$count]; [ $group, $host, $service, $state, $count, $ts +1, $ts2 ] } keys %{$ERROR1{$group}->{$host}->{$_}} } keys %{$ERROR1{$group}->{$_}} } keys %{$ERROR1{$_}} } keys %ERROR1;

The output of the above map is as below, I'm able to get the first occurrence of timestamp, but I want to get the last occurrence of the timestamps on the instead of "undef" value using the count mentioned above.

[ 'Oracle', 'server1', 'FILESYSTEM ALERT', 'WARNING 85% FULL', 8, '1433521920', { '' => undef } ], [ -- -

Any help will be greatly appreciated.

Replies are listed 'Best First'.
Re: Getting first and last element value of a hash loop
by FreeBeerReekingMonk (Deacon) on Jun 05, 2015 at 22:15 UTC

    Use Time::Seconds

    use Time::Seconds; my @LIST = map { [ $_->[0], $_->[1], $_->[2], $_->[3], $_->[4], $_->[5],$_->[6] + ] } sort { $a->[0] cmp $b->[0] || $b->[4] <=> $a->[4] || $a->[1] cmp $b-> +[1] || $a->[2] cmp $b->[2] || $a->[3] cmp $b->[3] } map { my $group = $_; map { my $host = $_; map { my $service = $_; map { my $state = $_; my $count = keys %{$ERROR1{$group}->{$host}->{ +$service}->{$state}}; my $ts1 = (sort (keys %{$ERROR1{$group}->{$hos +t}->{$service}->{$state}}))[0]; my $ts2 = (sort(keys %{$ERROR1{$group}->{$host +}->{$service}->{$state}}))[-1]; [ $group, $host, $service, $state, $count, Tim +e::Seconds->new($ts2-$ts1)->pretty() ] } keys %{$ERROR1{$group}->{$host}->{$_}} } keys %{$ERROR1{$group}->{$_}} } keys %{$ERROR1{$_}} } keys %ERROR1; for $_ (@LIST){ print join("\t", @{$_}) ."\n"; }
    Oracle server1 FILESYSTEM ALERT WARNING 85% FULL 8 + 23 hours, 30 minutes, 4 seconds Oracle server2 FILESYSTEM ALERT WARNING 85% FULL 5 + 21 hours, 59 minutes, 59 seconds Oracle server1 BACKUP ALERT DAILY 1 0 seconds

    And what the others are trying to say is that:

    my ($ts1,$ts2) = (sort (keys %{$ERROR1{$group}->{$host}->{$service}->{ +$state}}))[0,-1];

      I now imagine the map of Dora singing: I use map, I use map (bis).
      Timtowtdi... I know... but... for loops... for loops... this algorithm screams for loops... (my kingdom for four for loops)

        Thanks to all for your responses. It works for me now.

Re: Getting first and last element value of a hash loop
by aaron_baugher (Curate) on Jun 05, 2015 at 22:01 UTC

    Since a list starts at element 0, the index of the last element in the list is one less than the total number of elements. For instance, if the array @arr contains 5 elements, $arr[4] is the last. Try using [$count-1] as the index to get the last item in your sorted list.

    Update: parv's answer is better than mine, see below.

    Aaron B.
    Available for small or large Perl jobs and *nix system administration; see my home node.

Re: Getting first and last element value of a hash loop
by Laurent_R (Canon) on Jun 06, 2015 at 11:16 UTC
    I do not know how your %ERROR1 hash gets populated and whether it is also used for other purposes, but you might consider using a HoH...HoA rather than a HoH...HoH, i.e. the 'DAILY' and the 'WARNING 85% FULL' values might be array references instead of hash references, since the values in the most-nested hashes (always 1) seem to be fairly useless.

    Depending on where these values are populating from, there would be a high chance of having them sorted in the proper order so that you would not need to do any sort and could just pick up the first and last elements of these arrays.

    But of course, this is just a suggestion, you might not have the freedom to change this data structure for other reasons.