简体   繁体   中英

Perl, Sort nested hash

I have a data structure which looks like this (hash of hashes):

$VAR1 = {
          'masterkeyB' => {
                'D' => ['b', 'c', 'a'],
                'A' => ['b', 'a', 'c'],
                'C' => ['a', 'c', 'b'],
                'B' => ['a', 'c', 'b'],
              },
          'masterkeyA' => {
                'B' => ['a', 'c', 'b'],
                'C' => ['a', 'c', 'b'],
                'A' => ['b', 'a', 'c'],
                'D' => ['b', 'c', 'a'],
                }
        };

I would like to sort this structure in alphabetical order :

$VAR1 = {
          'masterkeyA' => {
                'A' => ['a', 'b', 'c'],
                'B' => ['a', 'b', 'c'],
                'C' => ['a', 'c', 'b'],
                'D' => ['a', 'b', 'c'],
              },
          'masterkeyB' => {
                'A' => ['a', 'b', 'c'],
                'B' => ['a', 'b', 'c'],
                'C' => ['a', 'c', 'b'],
                'D' => ['a', 'b', 'c'],
                }
        };

Note that all the arrays are sorted except for 'C'.

Just iterate over the keys in nested loops, sort the values as you encounter them. Edit: see http://p3rl.org/dsc#HASHES-OF-HASHES

use feature qw(postderef);
use Tie::Hash::Indexed qw();

my $unsorted = {
    'masterkeyB' => {
        'D' => ['b', 'c', 'a'],
        'A' => ['b', 'a', 'c'],
        'C' => ['a', 'c', 'b'],
        'B' => ['a', 'c', 'b'],
    },
    'masterkeyA' => {
        'B' => ['a', 'c', 'b'],
        'C' => ['a', 'c', 'b'],
        'A' => ['b', 'a', 'c'],
        'D' => ['b', 'c', 'a'],
    }
};

tie my %sorted, 'Tie::Hash::Indexed';
for my $k1 (sort keys $unsorted->%*) {
    for my $k2 (sort keys $unsorted->{$k1}->%*) {
        my $v = $unsorted->{$k1}{$k2};
        $v = [sort $v->@*] unless 'C' eq $k2;
        $sorted{$k1}{$k2} = $v;
    }
}
__END__
$HASH1 = {
    masterkeyA => {
        A => ['a', 'b', 'c'],
        B => ['a', 'b', 'c'],
        C => ['a', 'c', 'b'],
        D => ['a', 'b', 'c']
    },
    masterkeyB => {
        A => ['a', 'b', 'c'],
        B => ['a', 'b', 'c'],
        C => ['a', 'c', 'b'],
        D => ['a', 'b', 'c']
    }
};

Unlike arrays, hashes in perl don't stay in sorted order, so the common paradigm is to sort the hash keys while you're traversing the data structure. The basic code snippets you need are

for my $k ( sort keys %hash ) {
  for my $sk ( sort keys %{$hash->{$k}} ) {
    # do something
  }
}

to access hash keys in sorted order, and

if ( $k ne 'KEY' ) {
  # sort this array
  $hash->{$k} = sort @{$hash->{$k}}
}

to selectively sort arrays.

For example, say you're printing out the hash:

use strict;
use warnings;
use feature ':5.16';

my $data = {
      'masterkeyB' => {
            'D' => ['b', 'c', 'a'],
            'A' => ['b', 'a', 'c'],
            'C' => ['a', 'c', 'b'],
            'B' => ['a', 'c', 'b'],
          },
      'masterkeyA' => {
            'B' => ['a', 'c', 'b'],
            'C' => ['a', 'c', 'b'],
            'A' => ['b', 'a', 'c'],
            'D' => ['b', 'c', 'a'],
            }
    };


for my $mk ( sort keys %$data ) {
  say $mk;
  for my $letter ( sort keys %{$data->{$mk}} ) {
    say '  ' . $letter;
    if ( $letter eq 'C') {
      say '    [ ' . join(", ", @{$data->{$mk}{$letter}}) . ' ]';
    }
    else {
      say '    [ ' . join(", ", sort @{$data->{$mk}{$letter}}) . ' ]';
    }
  }
}

result:

masterkeyA
  A
    [ a, b, c ]
  B
    [ a, b, c ]
  C
    [ a, c, b ]
  D
    [ a, b, c ]
masterkeyB
  A
    [ a, b, c ]
  B
    [ a, b, c ]
  C
    [ a, c, b ]
  D
    [ a, b, c ]

Obviously you can change the say lines to do whatever you are planning to do with this data.

I think this does what you need. Not easy to ready but works.

my %sorted;
map {my $key = $_; map {$sorted{$key}->{$_} = ($_ eq 'C' ? $hash{$key}->{$_} : [sort @{$hash{$key}->{$_}}])} keys %{$hash{$key}}} keys %hash;

or sort original hash

map {my $key = $_; map {$hash{$key}->{$_} = ($_ eq 'C' ? $hash{$key}->{$_} : [sort @{$hash{$key}->{$_}}])} keys %{$hash{$key}}} keys %hash;

or shorter version

map {my $key = $_; map {$hash{$key}->{$_} = [sort @{$hash{$key}->{$_}}] if($_ ne 'C')} keys %{$hash{$key}}} keys %hash;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM