wagh wagh - 4 months ago 4
Perl Question

Generate array from reference hash

I'm trying to generate array from hash reference, created by joining all keys of hashes with sorting.
Consider I have dynamic hash reference like

my $hash_ref = {
'A1' => {
'B2' => {
'C1' => {
'D1' => {},
'D2' => {},
'D3' => {}
}
},
'B3' => {
'C1' => {
'D2' => {},
'D1' => {},
'D3' => {}
}
},
'B1' => {
'C1' => {
'D1' => {},
'D2' => {}
}
}
}
};


how to create array from above hash like

@arr = qw/A1B1C1D1 A1B1C1D2 A1B2C1D1 ..../;


below is the code I tried(which is not working)

my $out = hash_walk($hash_ref);

say Dumper $out;

sub hash_walk {
my $hash = shift;
my $array_ref;
my $temp_arr;
my @temp_arr2;
foreach my $k ( sort keys %$hash ) {
$v = $$hash{$k};

if ( ref($v) eq 'HASH' ) {

# Recurse.
$temp_arr = hash_walk( $v);

}
push @$array_ref, $k if $k;

my (@lvlfirst, @lvlnext );

if ($array_ref && $temp_arr){
@lvlfirst = @$array_ref;
@lvlnext = @$temp_arr;
}

for ( my $i = 0 ; $i <= $#lvlfirst ; $i++ ) {
for ( my $j = 0 ; $j <= $#lvlnext ; $j++ ) {
push @temp_arr2, "$lvlfirst[$i]$lvlnext[$j]"; ##Trying to join here

}
}
}

return \@temp_arr2;
}


XML is:

<root>
<class1 name="A1">
<class2 name="B1">
<class3 name="C1">
<class4 name="D1"></class4>
<class4 name="D2"></class4>
</class3>
</class2>
<class2 name="B2">
<class3 name="C1">
<class4 name="D1"></class4>
</class3>
</class2>
<class2 name="B3">
<class3 name="C1">
<class4 name="D1"></class4>
<class4 name="D2"></class4>
<class4 name="D3"></class4>
</class3>
</class2>
</class1>
</root>

Answer

You should really make some effort yourself before coming to SO for help. We're far more likely to help you fix broken code than just give you an answer.

But I'm feeling generous and I have a couple of minutes to spare.

The brute force approach would be to walk through every key at every level in the hash.

#!/usr/bin/perl

use strict;
use warnings;
use 5.010;

use Data::Dumper;

my $hash_ref = {
    'A1' => {
        'B2' => {
            'C1' => {
                'D1' => {},
                'D2' => {},
                'D3' => {}
            }
        },
        'B3' => {
            'C1' => {
                'D2' => {},
                'D1' => {},
                'D3' => {}
            }
        },
        'B1' => {
            'C1' => {
                'D1' => {},
                'D2' => {}
            }
        }
    }
};

my @arr;

for my $l1 (sort keys %$hash_ref) {
  for my $l2 (sort keys %{$hash_ref->{$l1}}) {
    for my $l3 (sort keys %{$hash_ref->{$l1}{$l2}}) {
      for my $l4 (sort keys %{$hash_ref->{$l1}{$l2}{$l3}}) {
        push @arr, "$l1$l2$l3$l4";
      }
    }
  }
}

say Dumper \@arr;

This produces the output:

$VAR1 = [
          'A1B1C1D1',
          'A1B1C1D2',
          'A1B2C1D1',
          'A1B2C1D2',
          'A1B2C1D3',
          'A1B3C1D1',
          'A1B3C1D2',
          'A1B3C1D3'
        ];

Update: Here's a recursive solution:

#!/usr/bin/perl

use strict;
use warnings;
use 5.010;

use Data::Dumper;

my $hash_ref = {
    'A1' => {
        'B2' => {
            'C1' => {
                'D1' => {},
                'D2' => {},
                'D3' => {}
            }
        },
        'B3' => {
            'C1' => {
                'D2' => {},
                'D1' => {},
                'D3' => {}
            }
        },
        'B1' => {
            'C1' => {
                'D1' => {},
                'D2' => {}
            }
        }
    }
};

my @arr = walk_hash($hash_ref, '');

say Dumper \@arr;

sub walk_hash {
  my ($hash_ref, $prefix) = @_;

  return $prefix unless keys %$hash_ref;
  return map { walk_hash($hash_ref->{$_}, "$prefix$_") } sort keys %$hash_ref;
}
Comments