sort hash of array by value

1

I am trying to sort by value in a HoA wherein key => [ a, b, c] I want to sort alphabetically and have tried and read with no success. I think its the commas, but please help! Below is a short snippet. The raw data is exactly how it appears in the data dumper print vs. the CLI. I have to use some sort of delimiter otherwise the cli output is tedious! Thank you!

use strict;
use warnings;

my ( $lsvm_a,$lsvm_b,%hashA,%hashB );
my $vscincludes  = qr/(^0x\w+)\,\w+\,\w+.*/;  #/

open (LSMAP_A, "-|", "/usr/ios/cli/ioscli lsmap -vadapter vhost7 -field clientid vtd backing -fmt ," ) or die $!;
while ($lsvm_a = (<LSMAP_A>)) {
     chomp($lsvm_a);
     next unless $lsvm_a =~ /$vscincludes/;
     @{$hashA{$1}} = (split ',', $lsvm_a);
}

open (LSMAP_B, "-|", "/usr/sbin/clcmd -m xxxxxx /usr/ios/cli/ioscli lsmap -vadapter vhost29 -field clientid vtd backing -fmt ," ) or die $!;
while ($lsvm_b = (<LSMAP_B>)) {
     chomp($lsvm_b);
     next unless $lsvm_b =~ /$vscincludes/;
     push @{$hashA{$1}}, (split ',', $lsvm_b);
}


print "\n\nA:";
for my $key ( sort { $hashA{$a} cmp $hashA{$b} } keys %hashA ) {
    print "$key => '", join(", ", @{$hashA{$key}}), "'\n";
}
##
print "===\nB:";
foreach my $key ( sort { (@{$hashB{$a}}) cmp (@{$hashB{$b}}) } keys %hashB ) {
    print "$key ==> @{$hashB{$key}}\n";
}

print "\n\n__DATA_DUMPER__\n\n";
use Data::Dumper; print Dumper \%hashA; print Dumper \%hashB;

Output

A:
0x00000008 => '0x00000008, atgdb003f_avg01, hdisk10, atgdb003f_ovg01, hdisk96, atgdb003f_pvg01, hdisk68, atgdb003f_rvg01, hdisk8, vtscsi0, atgdb003f_data.5bcd027df10f27bf9a880ce7bc1dd924'
===
B:
0x00000008 => '0x00000008, atgdb003f_avg01, hdisk10, atgdb003f_data, atgdb003f_data.5bcd027df10f27bf9a880ce7bc1dd924, atgdb003f_ovg01, hdisk96, atgdb003f_pvg01, hdisk68, atgdb003f_rvg01, hdisk8'

__DATA_DUMPER__

$VAR1 = {
          '0x00000008' => [
                            '0x00000008',
                            'atgdb003f_avg01',
                            'hdisk10',
                            'atgdb003f_ovg01',
                            'hdisk96',
                            'atgdb003f_pvg01',
                            'hdisk68',
                            'atgdb003f_rvg01',
                            'hdisk8',
                            'vtscsi0',
                            'atgdb003f_data.5bcd027df10f27bf9a880ce7bc1dd924'
                          ]
        };
$VAR1 = {
          '0x00000008' => [
                            '0x00000008',
                            'atgdb003f_avg01',
                            'hdisk10',
                            'atgdb003f_data',
                            'atgdb003f_data.5bcd027df10f27bf9a880ce7bc1dd924',
                            'atgdb003f_ovg01',
                            'hdisk96',
                            'atgdb003f_pvg01',
                            'hdisk68',
                            'atgdb003f_rvg01',
                            'hdisk8'
                          ]
        };

### CLI out ### 
            ###0x00000008,atgdb003f_avg01,hdisk10,atgdb003f_ovg01,hdisk96,atgdb003f_pvg01,hdisk68,atgdb003f_rvg01,hdisk8,vtscsi0,atgdb003f_data.5bcd027df10f27bf9a880ce7bc1dd924
        ###0x00000008,atgdb003f_avg01,hdisk10,atgdb003f_data,atgdb003f_data.5bcd027df10f27bf9a880ce7bc1dd924,atgdb003f_ovg01,hdisk96,atgdb003f_pvg01,hdisk68,atgdb003f_rvg01,hdisk8
arrays
perl
sorting
hash
asked on Stack Overflow Dec 11, 2019 by dirman • edited Dec 12, 2019 by zdim

1 Answer

3

Update   The arrayrefs (hash values) have multiple elements after all, and need be sorted. Then

for my $key (keys %h) { @{$h{$key}} = sort @{$h{$key}} }

or, more efficiently (and in the statement modifier form, with less noise but perhaps less clear)

$h{$_} = [ sort @{$h{$_}} ] for keys %h;

The sort by default uses lexicographical sort, as wanted.

Keys are desired to be sorted numerically, but note that while we can rewrite the arrays to make them sorted it is not so with hashes, which are inherently unordered. We can print sorted of course

foreach my $k (sort { $a <=> $b } keys %h) { ... } 

This will warn if keys aren't numbers.

By 56% – 60% in my benchmarks on three different machines, with both v5.16 and v5.30.0


Original post

I take it that you need to sort a hash which has an arrayref for a value, whereby that arrayref has a single element. Then sort on that, first, element

foreach my $key ( sort { $hashB{$a}->[0] cmp $hashB{$b}->[0] } keys %hashB ) {
    print "$key ==> @{$hashB{$key}}\n";
}

See the cmp operator under Equality operators in perlop. It takes scalars, which are stringwise compared (so the attempted sorting with an array from the question is wrong since cmp would get lengths of those arrays to sort by!)


In my understanding your hash to sort is like

$VAR1 = {
    '0x00000008' => [ 'atgdb003f_avg01,hdisk10,atgdb003f_ovg01,...' ],
    ...
}

where each value is an arrayref with exactly one element.

answered on Stack Overflow Dec 11, 2019 by zdim • edited Dec 14, 2019 by zdim

User contributions licensed under CC BY-SA 3.0