Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

A hash slice but not..

by smferris (Beadle)
on Feb 15, 2001 at 00:16 UTC ( [id://58435]=perlquestion: print w/replies, xml ) Need Help??

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

My apologies for the bad title. It's already taken me 30 minutes to come up with that! ;)

I use this a lot: (Very cool IMHO)

@hash{a..z}=(1..26)

What I'd really like to see though is something like this:

$hash{a..c}='some value'

Where the results would be equivalent to:

$hash{a}{b}{c}='some value'

Is there a cleaver way of doing this that would add the items. I had a subroutine (misplaced it) that would reverse the tree 'c'..'a' when passed 'a'..'c', loop on it, build a hash 'c'=>'some value', place that hash in 'b'=>{'c'='somevalue'} and finally 'a'=>{'b'=>{'c'='somevalue'}.

Obviously, if I ran the sub a second time with, (a,b,d)='another value', the 'b' branch (and 'a' for that matter) would be completely over-written. I'm not seeing the solution. Please help! 8)

All help will be greatly appreciated!
Shawn M Ferris
Oracle DBA

Replies are listed 'Best First'.
Re: A hash slice but not..
by chipmunk (Parson) on Feb 15, 2001 at 00:41 UTC
    For a specific case, you can just use the snippet you showed, of course: $hash{a}{b}{c}='some value'; When you specify nested references like that, Perl will autovivify the references that don't already point to something. (See perlref for more on autovivification.)

    If you're looking to do this with an arbitrary list, here's a subroutine that shows one way to do it:

    #!/usr/local/bin/perl -w use strict; sub set_nested_value { my($href, $keys, $value) = @_; my $last_key = pop @$keys; for my $key (@$keys) { $href = $href->{$key} ||= {}; } $href->{$last_key} = $value; } my %hash; set_nested_value(\%hash, ['a' .. 'c'], 'some value'); set_nested_value(\%hash, ['a', 'b', 'd'], 'other value'); use Data::Dumper; print Dumper \%hash;
    The for loop descends through the nested hashes. With the ||= operator, when there isn't already a hash reference at that level, it creates a new anonymous hash. $href is then reassigned to the next level in the nested hash.

    Note that this code assumes that each key will either point to a hash reference or to a value. If you set a value first, and then try to use it as a nested hash, you'll get an error from use strict. If you set a nested hash first, and then set a value at the same level, you'll delete the entire nested hash. Error checking could be added to the sub to handle those situations gracefully.

      What case does yours handle that his doesn't? Honestly, I think the elegant eval wins the prize here. Perl's autovivify doesn't have the problems you state yours has. I do rather like your array-ref style argument passing tho.

      Update: Nevermind, repeat after me kids, "eval is a four letter word". =)

      --
      $you = new YOU;
      honk() if $you->love(perl)

        The eval does not handle the keys being arbitrary data.

        I prefer the loop.

        Why does mine have to handle cases that his doesn't? It's just another way to do it. Anyway I didn't see his solution until after I posted mine, because I was testing my code and revising my text.

        Still, here's a case that mine handles differently from his:

        add2hash(\%tree, 1, ('print "Hello world!\n"')); set_nested_value(\%tree, ['print "Hello world!\n"'], 1);
        ;)

        BTW, relying on Perl's autovivify (as in the eval solution) does have the same problems mine has. In the snippet: $href->{a}{b}{c} = 7; If the value of $href->{a} is a string, then Perl will treat it as a symbolic reference and an error will occur under strict refs.

        Similarly, if the value of $href->{a}{b}{c} is a reference to another hash, then the above snippet will replace that hash reference with the value 7.

        Update: On second thought, just in case someone wants to run the test case, I replaced `rm -rf *` with print "Hello world!\n". :)

      Gotta say.. I like this one better. 8) I've already implemented it! Thanks a bunch to all!

      Shawn M Ferris
      Oracle DBA

Re: A hash slice but not..
by smferris (Beadle) on Feb 15, 2001 at 00:28 UTC

    Wouldn't you know it.. 10minutes after I send this, I remember eval! DOH! Following is what I've now come up with. Maybe someone has a better solution though.

    #!/app/perl5.005/bin/perl use strict; use Data::Dumper; sub add2hash { my($hash,$value,@tree)=@_; my $tree='$hash->{'.join('}{',@tree)."}=$value"; eval $tree; } my(%tree); add2hash(\%tree,1,('a'..'c')); add2hash(\%tree,2,('a','b','d')); print Dumper(\%tree);

    Regards,
    Shawn M Ferris
    Oracle DBA

      No. We've gone through this already here before. And before, I also said that this eval solution fails because you end up compiling code repeatedly, and you could get very bad results if the data contains certain strings.

      Do not use eval-string. Ever. Or at least until you understand all the drawbacks.

      -- Randal L. Schwartz, Perl hacker

      Change the my $tree='$hash->{'.join('}{',@tree)."}=$value"; line to my $tree='$hash->{'.join('}{',@tree).'}=$value'; or you will be REALLY unhappy the first time you send a string through... You don't want $value to be interpreted until the eval.

      Update: Nevermind, repeat after me kids, "eval is a four letter word". =)

      --
      $you = new YOU;
      honk() if $you->love(perl)

Re: A hash slice but not..
by extremely (Priest) on Feb 15, 2001 at 00:31 UTC
    try @hash{a..c}=('some value') x 3;

    Oh wait, you want it to NEST? bleagh... There is no easy shortcut for that. =)

    --
    $you = new YOU;
    honk() if $you->love(perl)

      He's not asking for a hash slice. He's asking for a shorter way to write $hash{this}{that}{those}.

      japhy -- Perl and Regex Hacker

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://58435]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others taking refuge in the Monastery: (3)
As of 2024-05-28 01:12 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found