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

Hello Monks,

I have a data structure which looks like this, And its a Hash which has arrayrefs as values. (based on inside array[4] i got to sort this structure.

$VAR1 = { '10857' => [ '10857', 'Test-host1', '192.168.162.124', undef, undef, undef, undef, 'Timeout while connecting to "192.168.162.124:1 +61"' ], '10715' => [ '10715', 'Test-Host2', '192.168.162.146', 'checkPing[{HOST.IP}]', '108', '108', '2014-04-01 20:24:01', '' ], '10778' => [ '10778', 'Test-Host3', '192.168.162.144', 'checkPing[{HOST.IP}]', '392', '359', '2014-04-01 20:21:12', '' ] '10582' => [ '10582', 'Test-Host3', '192.168.162.95', 'icmppingsec', '117.390000', '116.493000', '2014-04-01 20:23:03', '' ], };
Where i am looking at writing these values to excel using SpreadSheet::WriteExcel, and the values are sorted.
The value i am looking at sorting is 5th one. i.e
undef, 108, 392, 117.390000

Some places i get 'undef', some places 'NULL', some places INT, and some places 'float'.

Idea is to sort these based on 5th value and create array of arrays/hashes and loop through one by one and write it to excel. But unfortunately, i just can't think of way to do this. Any pointers are greatly helpful.


Thanks

Replies are listed 'Best First'.
Re: Need to sort and data structure based on values inside arrayrefs.
by Anonymous Monk on Apr 01, 2014 at 15:44 UTC
Re: Need to sort and data structure based on values inside arrayrefs.
by kcott (Archbishop) on Apr 02, 2014 at 05:48 UTC

    G'day shekarkcb,

    I got rid of the irrelevent data from the hash. I added more test data to better test the sorting process.

    I've assumed that (1) "'NULL'" means a zero-length string; (2) the wanted sort order is undefs first, then zero-length strings, then numerical data in ascending order; (3) the output is an array of arrays for writing to a spreadsheet.

    #!/usr/bin/env perl use strict; use warnings; my %data = ( A => [ 0, 1, 2, 3, 99, 5, 6 ], B => [ 0, 1, 2, 3, 9.9999, 5, 6 ], C => [ 0, 1, 2, 3, "", 5, 6 ], D => [ 0, 1, 2, 3, undef, 5, 6 ], E => [ 0, 1, 2, 3, 11, 5, 6 ], F => [ 0, 1, 2, 3, 123.0, 5, 6 ], G => [ 0, 1, 2, 3, '', 5, 6 ], H => [ 0, 1, 2, 3, undef, 5, 6 ], I => [ 0, 1, 2, 3, -11, 5, 6 ], J => [ 0, 1, 2, 3, -1.1, 5, 6 ], ); my @rows = map { [ $_->[0] => @{$data{$_->[0]}} ] } sort { (! defined $a->[1]) ? -1 : (! defined $b->[1]) ? 1 : (! length $a->[1]) ? -1 : (! length $b->[1]) ? 1 : ($a->[1] <=> $b->[1]) } map { [ $_ => $data{$_}[4] ] } keys %data; use Data::Dump; dd \@rows;

    Output:

    [ ["H", 0 .. 3, undef, 5, 6], ["D", 0 .. 3, undef, 5, 6], ["C", 0 .. 3, "", 5, 6], ["G", 0 .. 3, "", 5, 6], ["I", 0 .. 3, -11, 5, 6], ["J", 0 .. 3, -1.1, 5, 6], ["B", 0 .. 3, 9.9999, 5, 6], ["E", 0 .. 3, 11, 5, 6], ["A", 0 .. 3, 99, 5, 6], ["F", 0 .. 3, 123, 5, 6], ]

    -- Ken

      Very nice solution. I was also thinking of a succession of ternary operators to handle the undef or "null" special cases, but did not want in my earlier post to decide on those cases without the OP specifying the required behavior for those cases (which is why I suggested an example sorting on another field). Having said that, if the idea is just to sort the (final) sub-arrays (as I understood the OP's requirement), then the two calls to the map function seem unnecessary. We could have just this (using your test data):
      use strict; use warnings; my %data = ( A => [ 0, 1, 2, 3, 99, 5, 6 ], B => [ 0, 1, 2, 3, 9.9999, 5, 6 ], C => [ 0, 1, 2, 3, "", 5, 6 ], D => [ 0, 1, 2, 3, undef, 5, 6 ], E => [ 0, 1, 2, 3, 11, 5, 6 ], F => [ 0, 1, 2, 3, 123.0, 5, 6 ], G => [ 0, 1, 2, 3, '', 5, 6 ], H => [ 0, 1, 2, 3, undef, 5, 6 ], I => [ 0, 1, 2, 3, -11, 5, 6 ], J => [ 0, 1, 2, 3, -1.1, 5, 6 ], ); my @rows = sort { (! defined $a->[4]) ? -1 : (! defined $b->[4]) ? 1 : (! length $a->[4]) ? -1 : (! length $b->[4]) ? 1 : ($a->[4] <=> $b->[4]) } values %data;
      But here again, we don't really know what the OP exactly wants to get.

        What you have there is exactly (or extremely close to) what I originally coded; however, after considering the OP's "writing these values to excel", I quickly changed that to a Schwartzian Transform.

        You're quite correct about not knowing what the OP really wanted. With any of the suggested solutions, questions regarding how an undef is written to a spreadsheet remain outstanding (and this would almost certainly require further coding).

        -- Ken

      Thank you all for the reply. This is how i achieved the result.

      1). Created another hash with 5th entry as value.
      2). Sorted the that hash, moved keys to an array

      my @sorted = sort { $subHash{$b} <=> $subHash{$a} } keys %subHash;
      , @sorted had the required output.
      Thanks agin for the pointers, closing the thread

      Thanks
Re: Need to sort and data structure based on values inside arrayrefs.
by Laurent_R (Canon) on Apr 01, 2014 at 15:51 UTC
    Hi, sorting on the first field of the arrays instead of the 5th one, because you did not specify what to do with undef values (i.e. how to sort them):
    use strict; use warnings; use Data::Dumper my %hash = ( '10857' => [ '10857', 'Test-host1', '192.168.162.124', undef, undef, undef, undef, 'Timeout while connecting to "192.168.162.124:1 +61"' ], '10715' => [ '10715', 'Test-Host2', '192.168.162.146', 'checkPing[{HOST.IP}]', '108', '108', '2014-04-01 20:24:01', '' ], '10778' => [ '10778', 'Test-Host3', '192.168.162.144', 'checkPing[{HOST.IP}]', '392', '359', '2014-04-01 20:21:12', '' ], '10582' => [ '10582', 'Test-Host3', '192.168.162.95', 'icmppingsec', '117.390000', '116.493000', '2014-04-01 20:23:03', '' ] ); my @sorted_array = sort {$a->[0] <=> $b->[0] } values %hash; print Dumper \@sorted_array;
    You'll have to change the
    {$a->[0] <=> $b->[0] }
    part to something like: { (do something for undef values;) $a->4 <=> $b->4 }

    The result:

        Yes, this is one way to do it. I just did not want to decide for the OP what to do for undef, empty strings and other such special cases where it is difficult to figure out how to sort them without knowing the functional context. I'll gladly update my proposal when (and if) the OP gives additional information.