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

I want to take an array and turn it into a hash nested as far deep as the number of elements in the array. For example:
@foo = qw[1 2 3 4 5]; should transform into a hash that looks like this $bar->{1}{2}{3}{4}{5} = undef;
I have code that works but I'm not terribly happy with it and I know there is some foo I'm missing somewhere. Below is the code I have working:
use Data::Dumper; use strict; use warnings; my $hashref; my @foo = qw[1 2 3 4 5]; my $string = "\$hashref->"; foreach my $item (@foo) { $string .= "{$item}"; } $string .= "= undef"; eval($string); print Dumper($hashref);
As I said, this works but I feel there is probabbly a much more perlish way to do it. I searched a bunch of different places and didn't find much other than the normal perldsc type stuff.

Replies are listed 'Best First'.
Re: Array to nested hash
by davido (Cardinal) on Nov 08, 2004 at 16:48 UTC

    Do it backwards.

    use strict; use warnings; use Data::Dumper; my @foo = qw/1 2 3 4 5 6/; my $href; foreach my $key ( reverse @foo ) { $href = { $key, $href }; } print Dumper $href;

    This also turns out to be very similar to one of the easiest ways in Perl to build a traditional ( in the tradition of computer science ) linked list.


    Dave

Re: Array to nested hash
by tinita (Parson) on Nov 08, 2004 at 16:52 UTC
    my $hashref = {}; my $ref = \$hashref; for (@foo) { $ref = \$$ref->{$_}; } $$ref = 1; # or the value you want it to be print Dumper $hashref;

    update: this also works if you want to insert more than one array, while the backwards approach will likely destroy previously built data structures

Re: Array to nested hash
by Limbic~Region (Chancellor) on Nov 08, 2004 at 16:51 UTC
    amw1,
    No need to use evil eval, this should do it:
    #!/usr/bin/perl use strict; use warnings; my @foo = 1..5; my %hash; my $ref = \%hash; for ( 0 .. $#foo ) { $ref->{ $foo[$_] } = $_ == $#foo ? undef : {}; $ref = $ref->{ $foo[$_] }; }
    The question is - why?

    Cheers - L~R

      The question is - why?
      I needed this once, too. Dynamically building data structures, usually if the data (here the array) comes from outside the program.

      I also once maintained a script which used the eval approach. The script was twice as fast after I replaced it with my code.

      The question is - why?

      Homework?

        No, not homework. Trying to generate a nested data structure from a path statement that looks like
        level1::level2::level3::leaf
        for a menuing system.
Re: Array to nested hash
by tmoertel (Chaplain) on Nov 09, 2004 at 02:04 UTC
    This is a fine opportunity to use List::Util's reduce function:
    use List::Util qw( reduce ); sub array_to_nested_hash { reduce { scalar {$b,$a} } undef, reverse @_; }
    Now, to test our subroutine:
    use Data::Dumper; $Data::Dumper::Terse = 1; $Data::Dumper::Indent = 0; print Dumper( array_to_nested_hash(1..5) ), "\n"; # {'1' => {'2' => {'3' => {'4' => {'5' => undef}}}}}
    Cheers,
    Tom
Re: Array to nested hash
by habit_forming (Monk) on Nov 08, 2004 at 17:09 UTC
    This is my best guess. =)
    #!/usr/bin/perl use strict; use warnings; use Data::Dumper; my @a = qw(1 2 3 4 5 6 7 8 9); my $h = {}; my $t = $h; map { ($t = $t->{$_} = ($_ eq $a[-1])? undef : {}) } @a; print Dumper($h);

    But as always, use at your own risk.

    Cheers!
    --habit
Re: Array to nested hash
by Grygonos (Chaplain) on Nov 08, 2004 at 17:50 UTC

    Recursive solution

    use strict; use warnings; use Data::Dumper; my %uberhash; my @test = qw[1 2 3 4 5]; getFunky(\@test,\%uberhash); print Dumper(%uberhash); sub getFunky { my ($arr,$hash) = @_; if($#$arr != -1) { my $current = shift @{$arr}; if($#$arr == -1 ) {getFunky($arr,\$$hash{$current});} else {getFunky($arr,\%{$$hash{$current}});} } else { $hash => undef; return; } }
    Don't know if that's any more a perl way to do it.. but still a different way.

Re: Array to nested hash
by NetWallah (Canon) on Nov 09, 2004 at 00:56 UTC
    Somewhat cleaner recursion here..
    use Data::Dumper; my $a; sub x{ my $ar=$_[1]; my $v=shift @$ar or return $_[0]=undef; x($_[0]->{$v} , $ar) }; x($a,[5,3,4,5,6]); print Dumper \$a;
    Prints..
    $VAR1 = \{ '5' => { '3' => { '4' => { '5' => { '6' => undef } } } } };

        Earth first! (We'll rob the other planets later)

Re: Array to nested hash
by TedPride (Priest) on Nov 09, 2004 at 09:16 UTC
    use Data::Dumper; use strict; use warnings; my @foo = qw[1 2 3 4 5]; my $bar; for ($_ = $#foo; $_ >= 0; $_--) { $bar = {$foo[$_], $bar}; } print Dumper($bar);