Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

setting hash keys by array

by hotshot (Prior)
on Sep 09, 2002 at 15:57 UTC ( #196317=perlquestion: print w/replies, xml ) Need Help??
hotshot has asked for the wisdom of the Perl Monks concerning the following question:

Hello fellow monks!

I have an array of unfixed size (it changes every time depending on a split command), and I want to create a hash which it's keys are by the array's content, for example:
my @array = qw(red green brown); # I want to get $hash{red}{green}{brown} = 1;
Anyone has an idea how it can be done in a short way? (I remind you that I can't count on fixed sized array)

Thanks

Hotshot

Replies are listed 'Best First'.
Re: setting hash keys by array
by japhy (Canon) on Sep 09, 2002 at 16:27 UTC
    Here's how I usually do it:
    use Data::Dumper; my @keys = qw( this that those ); my $node = \\my %hash; # edit: this was my old way... # my $i = 0; # $node = \$$node->{$keys[$i++]} while $i < @keys; # thanks to Juerd $node = \$$node->{$_} for @keys; $$node = "value"; print Dumper \%hash;

    _____________________________________________________
    Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

      $node = \$$node->{$keys[$i++]} while $i < @keys;

      ++. IMHO, this is the only sane approach. But why not $node = \$$node->{$_} for @keys;?

      Update
      Reputation: 41, it says. MADNESS. I only suggested the use of for LIST instead of explicitly using an index variable. It's a style related comment. Why on earth did you people upvote this THIS MUCH? There are better nodes getting lower scores. Vote for those. ++ japhy, ++ demerphq for they did the work. I only complained about style.

      - Yes, I reinvent wheels.
      - Spam: Visit eurotraQ.
      

        Nice stuff to both you and japhy

        While trying to grok that code I stumbled on an interesting different approach... Its not suitable for building a tree from many lists, but is suitable for building a HOH out of a single list:

        use Data::Dumper; my @keys = qw( this that those ); my $node='value'; $node={$_=>$node} foreach reverse @keys; print Dumper $node; __END__ $VAR1 = { 'this' => { 'that' => { 'those' => 'value' } } };

        --- demerphq
        my friends call me, usually because I'm late....

        Why not:

        my @chain = qw/a b c d/; my %hash; sub foo(\%@){ local $_ = shift; $_ = $_->{scalar shift} = {} while @_; } foo %hash => @chain;

        ? it's becoming obfuscated ...

        --
        http://fruiture.de
        My mistake -- I lifted it from old code of mine. Recent versions use your suggested alteration.

        _____________________________________________________
        Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
        s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

      Thanks for your answer !!, but can you give me a little explenation, what exactly it does and the syntax (especially lines 3)
      1) use Data::Dumper; 2) my @keys = qw( this that those ); 3) my $node = \\my %hash; 4) $node = \$$node->{$_} for @keys; 5) $$node = "value"; 6) print Dumper \%hash;
      I prefer understanding this before using it.
      Thanks

      Hotshot
        Yeah this is a nasty cryptic and totally groovy way to do things. I think I understand how it works, let see if I am right...
        my $node=\\my %hash;
        This sets $node to be a reference to a reference to %hash.
        $node = \$$node->{$_} for @keys;
        So this says to make node become equal to a reference to the slot $_ in the current hashref that $node references (foreach element in the list). It might be clearer to write it like so:
        $node = \(${$node}->{$_})
        Basically the idea of this is to take advantage of perls autovivification. The double reference is a nice way to ensure that we are always pointed at the slot that holds the previous key. If that value is undef, then it will be autovivified when it is used as a reference.

        $$node = "value";
        Stick the final value in.

        Cool eh? juerd++ and japhy++

        --- demerphq
        my friends call me, usually because I'm late....

      Here's how I usually do it:

      Is this something that you usually need to do? :P

      -Lee

      "To be civilized is to deny one's nature."
        Har har. First, you misunderstand my sentence. Second, it's done more often than you might think. I have a friend at RiskGrades who asked for some help setting up a hash in this way. She gets a list of keys, and needs to build up a hash (of hashes)+ based on those keys.

        _____________________________________________________
        Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
        s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

Re: setting hash keys by array
by broquaint (Abbot) on Sep 09, 2002 at 16:16 UTC
    This seems to do the trick
    use Data::Dumper; my @a = split ' ', q[foo bar baz quux]; my $le = my $h = {}; for(0 .. $#a - 1) { $le->{ $a[$_] } = { $a[$_ + 1] => {} }; $le = $le->{ $a[$_] }; } print Dumper $h; __output__ $VAR1 = { 'foo' => { 'bar' => { 'baz' => { 'quux' => {} } } } };
    Although are you sure this is the trick you want to be performing?
    HTH

    _________
    broquaint

Re: setting hash keys by array
by blssu (Pilgrim) on Sep 09, 2002 at 17:31 UTC
    sub nested { return ( shift, (@_) ? { nested(@_) } : 1 ) } my %hash = nested(qw(red green brown));
Re: setting hash keys by array
by Aristotle (Chancellor) on Sep 09, 2002 at 17:05 UTC
    my @array = qw(red green brown); my %hash; my $ptr = \%hash; $ptr = $ptr->{$_} = {} for @array; $ptr = 1;

    Makeshifts last the longest.

      That code (and several other examples) don't work quite like the request:

      $hash{red}{green}{brown} = 1

      They do this instead:

      $hash{red}{green}{brown} = { }

      That inner-most key makes a lot of the foreach solutions kind of ugly because the last key must be treated specially -- unless there's a way to transmogrify a hash ref into a scalar ref? Here's a short reverse foreach that uses globs to solve the corner case:

      my @array = qw(red green brown); use vars qw(%hash); foreach (reverse @array) { *hash = { $_ => (%hash) ? \%hash : 1 } }

      The lexical version is more ugly and a lot less efficient:

      my @array = qw(red green brown); my %hash; foreach (reverse @array) { %hash = ( $_ => (%hash) ? { %hash } : 1 ) }
        They do this instead: $hash{red}{green}{brown} = { }
        True, that's what comes out of the for loop. Then the next instruction in my sample turns it into the requested result.. Is the following more to your liking?
        my $ptr = \%hash; $ptr = $ptr->{$_} = {} for @array[0..$#array-1]; # fixed; s/shift @arr +ay/$_/ $ptr->{$array[-1]} = 1;
        or maybe
        my $ptr = \%hash; $ptr = $ptr->{shift @array} = {} while @array > 1; $ptr->{$array[0]} = 1;
        Update: forgot to remove the shift when I initially copypasted. See comment.

        Makeshifts last the longest.

Re: setting hash keys by array
by Shendal (Hermit) on Sep 09, 2002 at 16:13 UTC
    How about something like this?
    #!/usr/bin/perl -w use strict; use Data::Dumper; my @ary = qw(red green brown); my %hash = (); eval '$hash{' . join('}{',@ary) . '}++;'; print Dumper(\%hash);

    That prints:
    $VAR1 = { 'red' => { 'green' => { 'brown' => '1' } } };

    Cheers,
    Shendal
      This is probably not a great way to do it... not only is it inefficient and open to syntax errors (i.e. a key of ';}' would be bad), but if this was something that used user input it could be a security hole... ( @ary = '},`rm -rf /`,{' )

      eval is usually best used as a last resort and for exception handling (though this is usually with existing written code)

                      - Ant
                      - Some of my best work - (1 2 3)

Re: setting hash keys by array
by shotgunefx (Parson) on Sep 09, 2002 at 16:09 UTC
    What you've specified is a hash of a hash of a hash. I don't think that's what you meant. If you meant each array value ends in a single hash with a value of 1, the below will work.
    my @array =(a..f); my %h = map { $_ => 1 } @array;


    -Lee

    "To be civilized is to deny one's nature."
Re: setting hash keys by array
by boo_radley (Parson) on Sep 09, 2002 at 16:14 UTC
    here's one way.
    my @foo = qw (brown green red); $e .= "{$_}" foreach @foo; $e= "\$bar$e=1"; eval $e;
Re: setting hash keys by array
by artist (Parson) on Sep 09, 2002 at 16:25 UTC
    Hi
    Many solutions are given and with the message if this is what you want to do.
    If not experimental base, explain us what's the purpose and we can certainly come up with something better.

    Practicing art of hashing
    an artist.

Re: setting hash keys by array
by Chmrr (Vicar) on Sep 10, 2002 at 01:45 UTC

    See Re: descending a tree of hash references for a discussion of the same problem. Most of the solutions in this thread are handy, but sometimes it's useful to be able to wrap it into a subroutine.

    perl -pe '"I lo*`+$^X$\"$]!$/"=~m%(.*)%s;$_=$1;y^`+*^e v^#$&V"+@( NO CARRIER'

Re: setting hash keys by array
by the_slycer (Chaplain) on Sep 09, 2002 at 16:10 UTC
    $hash{$_}++ foreach @array

    Update:
    Misunderstood the question. Ignore me :P
Re: setting hash keys by array
by Felonious (Chaplain) on Sep 10, 2002 at 14:46 UTC
    my @arr = qw(red green brown); my $hash; $hash = { $_ => $hash || 1 } for (reverse @arr);
    -- O thievish Night, Why should'st thou, but for some felonious end, In thy dark lantern thus close up the stars? --Milton
Re: setting hash keys by array
by Anonymous Monk on Sep 10, 2002 at 15:56 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://196317]
Approved by broquaint
Front-paged by wil
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (2)
As of 2018-11-18 12:59 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My code is most likely broken because:
















    Results (205 votes). Check out past polls.

    Notices?