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

Hi, I was wondering if there is any very quick way of doing the following:

1/ Array of arrays:
[ ["a","b","c"]
["a","b","d"]
["b","b","b"]
]

2/ Transform into hash:

H->{"a"} -> {"b"} -> {"c"} = 1
| branched to "b" \-> {"d"} = 1
\-> {"b"} -> {"b"} -> {"b"} = 1

I figured out how to do using recursivity but was wondering if anything simplier is possible, maybe with "eval"? Thanks for any hint,
  • Comment on How to transform a matrix into a hash??

Replies are listed 'Best First'.
Re: How to transform a matrix into a hash??
by kyle (Abbot) on Feb 05, 2008 at 17:47 UTC

    I would not recommend using eval. What's wrong with the recursive solution you have? If you really want to eval:

    use Data::Dumper; my @start = ( [qw( a b c )], [qw( a b d )], [qw( b b b )], ); my %finish; my $e = join '', map { join '', '$finish', ( map {qq{{'$_'}}} @$_ ), " = 1;\n" } @start; print $e, "\n"; eval $e; print Dumper \%finish; __END__ $finish{'a'}{'b'}{'c'} = 1; $finish{'a'}{'b'}{'d'} = 1; $finish{'b'}{'b'}{'b'} = 1; $VAR1 = { 'a' => { 'b' => { 'c' => 1, 'd' => 1 } }, 'b' => { 'b' => { 'b' => 1 } } };

    Update: In addition to saying that eval is probably not the best way to do this, I should have added that my particular implementation is not as careful as it should be. If this were really to be used, it would be a good idea to (1) better quote the indices into the hash, and (2) check the value of $@ (aka $EVAL_ERROR if you use English, see perlvar) in case of error.

      Yikes! If you're going to use eval do a better job of converting your strings to literals!

      Better yet, don't use eval at all.

      my %finish; for my $indexes (@start) { my $p = \\%finish; $p = \($$p->{$_}) for @$indexes; $$p = 1; }

      Data::Diver hides that mess away.

Re: How to transform a matrix into a hash??
by starX (Chaplain) on Feb 05, 2008 at 17:44 UTC
    Not sure if it's simpler than your way of doing it, but I think something like this would work:
    my $HoH = {}; foreach my $array (@AoA) { $HoH->{ $array->[0] }->{ $array[1] }->{ $array->[2] } = 1; }
    But I haven't tested it, so YMMV.
Re: How to transform a matrix into a hash??
by moklevat (Priest) on Feb 05, 2008 at 17:45 UTC
    If you throw some <code> </code> tags around your structures, things will align better and be more readable. Your AoA is clear, but I am unable to discern what you what you want the resulting hash to look like. If you can construct it in terms of key => value pairs, then someone (perhaps me) can help with the transformation.

    Update: It looks like other monks better understood your needs.

Re: How to transform a matrix into a hash??
by bobf (Monsignor) on Feb 06, 2008 at 03:33 UTC

    IMO, Data::Diver is definitely the way to go here. No fuss, no muss - just DWIMery.

    use strict; use warnings; use Data::Diver qw( DiveVal ); use Data::Dumper; my $aokeys = [ [ 'a', 'b', 'c' ], [ 'a', 'b', 'd' ], [ 'b', 'b', 'b' ], ]; my $href = {}; foreach my $key_aref ( @$aokeys ) { DiveVal( $href, @$key_aref ) = 1; } print Dumper $href;
    $VAR1 = { 'a' => { 'b' => { 'c' => 1, 'd' => 1 } }, 'b' => { 'b' => { 'b' => 1 } } };

      Thanks all for your suggestions!
      I never used 'eval', but from what I've read, it seems like a good thing!
      The recursive way of doing it has nothing wrong, but I was wondering if there were other ways - to learn new tricks :)
      Thanks again! Maybe I can actually post the recursive way:
      my @array_2D = blablabla my ($tree, $tmp); foreach my $row (@array){ $tmp = extend_MTree($row); $tree = add($tmp, $tree); } $tree now contains the hash! sub extend_MTree($) { my ($a) = @_; my %H_tmp; if( $#{$a} == -1) { return 1; } my $elmt = shift @$a; $H_tmp{$elmt} = extend_MTree($a); return \%H_tmp; } sub add() { my ($tmp, $res) = @_; foreach my $key (keys %$tmp) { # this is a new path, add here and leave if(!defined($res->{$key})) { $res->{$key} = $tmp->{$key}; return $res; } # this part of the path exists, recurse $res->{$key} = add($tmp->{$key}, $res->{$key}); return $res; }