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

does anyone have a more _concise_ (read: no temporary) way of doing this?
while (($key, $lref) = each %HoL)
{
    my %h = ();
    @h{@{$lref}} = (1) x @{$lref};
    $HoL{$key} = \%h;
}

Replies are listed 'Best First'.
Re: hash of lists - hash of hashes
by chromatic (Archbishop) on Jun 13, 2000 at 04:43 UTC
    I think this does what you want:
    %hoh = map { $_, { map { $_, 1 } @{ $hol{$_}} } } keys %hol;
    Here's what I used to test:
    #!/usr/bin/perl -w use strict; my %hol = ( first => [ 1, 2, 3 ], second => [ 2, 4, 6 ] ); my %hoh; %hoh = map { $_, { map { $_, 1 } @{ $hol{$_}} } } keys %hol; walk_hash(\%hoh); sub walk_hash { my $h_ref = shift; my $indent = shift; $indent ||= 0; foreach my $key (keys %$h_ref) { print "\t" x $indent, "$key => "; my $value = $h_ref->{$key}; if (ref($value) eq 'HASH') { print "\n"; walk_hash($value, $indent + 1); } else { print "\t" x $indent, "$value\n"; } } }
    And, yes merlyn, you get a bonus hash-walking algorithm for free. It's two posts for the price of one here on Perl Monks (I will use Data::Dumper next time, I will use Data::Dumper next time...)
Re: hash of lists - hash of hashes
by lhoward (Vicar) on Jun 13, 2000 at 04:03 UTC
    This is the best that I've managed so far:
    while (($k, $lref) = each %HoL){ $HoL{$k}={map {$_,1} @$lref}; }
    I tried to get something along the lines of the following bit of code to work but couldn't:
    %HoL=map {map {$_,1} @HoL{$_}} keys %HoL;
    I'm sure merlyn could blow this out of the water (and I hope he does, I'd love to see his solution... probably something really slick w/ 2 maps)
      oooh
clarification...
by visnu (Sexton) on Jun 13, 2000 at 04:26 UTC
    for merlyn:
    %HoL is a hash of lists.
    i'd like to change it into a hash of hashes.
    each contained, secondary hash would consist of keys from the members of the original list, with corresponding values of just '1'.

    not sure if i can be more clear.. probably can; i'm just not good at it.

    WAIT! pictures!!

    i what to change this:
    %HoL = ( fruits     =>  "orange", "banana", "kiwi" ,
             vegetables =>  "carrot", "potato" ,
             cheeses    =>  "cheddar", "american" 
           );
    
    to this:
    %HoH = ( fruits     => { orange => 1, banana => 1, kiwi => 1 },
             vegetables => { carrot => 1, potato => 1 },
             cheeses    => { cheddar => 1, american => 1}
            );
    
clarification...
by visnu (Sexton) on Jun 13, 2000 at 04:28 UTC
    hmm, preview gets rid of my brackets.... to repeat,
    i what to change this:
    %HoL = ( fruits     => [ "orange", "banana", "kiwi" ],
             vegetables => [ "carrot", "potato" ],
             cheeses    => [ "cheddar", "american" ]
           );
    
    to this:
    %HoH = ( fruits     => { orange => 1, banana => 1, kiwi => 1 },
             vegetables => { carrot => 1, potato => 1 },
             cheeses    => { cheddar => 1, american => 1}
            );
    
      In place, you could do something like this:
      for (@HoL{keys %HoL}) { $_ = {map {$_, 1} @$_}; }
      (You can use values in place of the slice in 5.6, but I'm not there yet.)

      If you really need a copy, that's a little odder, like this:

      %HoH = map { $_ => {map { $_, 1 } @{$HoL{$_}}} } keys %HoL;
      Untested, but I think I got my indirections right.

      -- Randal L. Schwartz, Perl hacker

extended problem
by visnu (Sexton) on Jun 13, 2000 at 05:02 UTC
    in case anyone wanted something to think about instead of other work, the original (extended) problem i want to solve was to suppose those lists contained other hash keys.. so now the problem is such:

    %HoL = ( bob    => [ "mary", "ken"   ],
             aditya => [ "cary", "bob"   ],
             devil  => [ "bob", "aditya" ]
           );
    
    and i then finally want:
    %HoL = ( bob    => { mary => 1, ken => 1 },
             aditya => { cary => 1, bob => 1, mary => 1, ken => 1 },
             devil  => { bob => 1, mary => 1, ken => 1, aditya => 1, cary => 1 }
           )
    
    so now everything shows itself readily when we use the hash, or something like that. and of course, one shouldn't get into a recursive loop, etc, blah blah blah
      You posted the same question again. My code does that already. If you meant to convert a list-of-list-of-lists into a hash-of-hash-of-hashes (recursively), it's pretty straight forward.

      -- Randal L. Schwartz, Perl hacker

      this is actually what i finally came up with:

      my ($key, $value, $len); while (($key, $value) = each %HoL) { my $href = $HoL{$key} = { map {$_, 1} @$value }; do { $len = keys %$href; for (@HoL{@$value}) { if (ref eq "HASH" ) { @$href{keys %$_} = (1) x (keys %$ +_) } elsif (ref eq "ARRAY") { @$href{@$_} = (1) x @$_ } } $value = [ keys %$href ]; } while (@$value > $len); }
      it's iterative no less. any comments?
        on second run, this code is crack-headed. it ends up modifying the top level hash that it's iterating over and runs into bad stuff.
        note: that's me forgetting to login, up there posting..