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

What I have working at the moment is a hash that is setup with two embedded map{} statements. This setup allows you to declare multiple hash keys to have the same value in a eye-pleasing manner. Now what I need to accomplish is one step further: populating this hash from an array with elements that will be splitted to extract the data to place into the hash.

Here's the code I have so far (working code):

my %hash = map { my $item = pop @$_; map { $_, $item } @$_ } [ ( 'key1', 'key2', 'key3' ) => 'Value for key1, key2, and key3' ], [ qw( key4 key5 key6 ) => 'Value for key4, key5, key6' ], [ ( 'key7', 'key8', 'key9' ) => 'Value for key7, key8, key9' ]; # So we can now do the following. Hopefully everyone # now understands what the hash declaration above does # Output shown after __END__ while ( my($k,$v) = each %hash ) { print "$k: $v\n"; } __END__ key1: Value for key1, key2, key3 key2: Value for key1, key2, key3 key3: Value for key1, key2, key3 key4: Value for key4, key5, key6 key5: Value for key4, key5, key6 etc etc etc

Okay perfect! I can now set multiple hash keys to contain the same value. This is precisely what I needed and I got that working. Now, the tougher part that I simply cannot grasp onto? How to fill that same hash, populating it from an array. The ideal array for my case here would be the key names separated by a tilde ('~'), and the value for those keys to be at the end of the element, separated from the key names by a newline ('\n'). I could live with there needing to be a tilde before the newline. So the array for the above example would look exactly like this:

my @data = ( qq{key1~key2~key3 Value for key1, key2, key3}, qq{key4~key5~key6 Value for key4, key5, key6}, qq{key7~key8~key9 Value for key7, key8, key9} );

Any suggestions as to how one would take the above array and create the hash I posted even further above? I know exactly how I want everything, now if I only had the brain power to figure it all out :)


      C:\>shutdown -s
      >> Could not shut down computer:
      >> Microsoft is logged in remotely.
    

update (broquaint): title change (was Split array to populate a hash with multiple keys pointing to same value)

Replies are listed 'Best First'.
Re: Splitting an array to populate hash
by Pardus (Pilgrim) on Jan 29, 2003 at 10:51 UTC
    This is how I would implement this:
    my @data = ( qq{key1~key2~key3 Value for key1, key2, key3}, qq{key4~key5~key6 Value for key4, key5, key6}, qq{key7~key8~key9 Value for key7, key8, key9} ); my %hash; for (@data) { s/\n(.*)$//; %hash = ( %hash, map {$_, $1} split(/~/, $_)); }
    Also possible:
    my %hash = map { my @things = split(/~|\n/, $_); my $value = pop @things; map { $_, $value } @things; } @data;

    --
    Jaap Karssenberg || Pardus (Larus)? <pardus@cpan.org>
    >>>> Zoidberg: So many memories, so many strange fluids gushing out of patients' bodies.... <<<<
Re: Splitting an array to populate hash
by bart (Canon) on Jan 29, 2003 at 10:57 UTC
    Let me guess... LPC? :-) There have been a few questions in that direction lately. I had not heard of it before.

    Perhaps you should use an anonymous array for a "key", instead of a list. You then no longer need to wrap each pair in an anonymous array. The resulting code would look more Perlish, IMO.

    my %hash = expand_keys( ['key1', 'key2', 'key3'] => 'Value for key1, key2, and key3', [qw( key4 key5 key6) ] => 'Value for key4, key5, key6', 'simple key' => 'Value for simple key' );
    Now it's just a matter of defining expand_keys():
    sub expand_keys { my @result; while(@_) { my $key = shift; my $value = shift; if(ref $key) { push @result, $_, $value foreach @$key; } else { push @result, $key, $value; } } return @result; }
    You could populate a hash instead of pushing the data onto an array, in the sub, I suppose. I think it might be a tiny bit slower, because you would then be building two hashes, instead of just one (and an array).
Re: Splitting an array to populate hash
by BrowserUk (Patriarch) on Jan 29, 2003 at 11:21 UTC

    Seems to me that your laying your one-to-many data as many-to-one. I think I'd lay it out and populate the hash this way. Nested maps aren't to everyones tastes, but they make light work of the job at hand.

    #! perl -slw use strict; use Data::Dumper; my %hash = map{ local $a=$_; map{ $_ => $a->[0] } @{$a->[1]} } ( [ value_for_keys_1_2_3 => [ qw[key1 key2 key3] ] ], [ value_for_keys_4_5_6 => [ qw[key4 key5 key6] ] ], [ value_for_keys_7_8_9 => [ qw[key7 key8 key9] ] ], ); print Dumper \%hash; __END__ c:\test>230905 $VAR1 = { 'key7' => 'value_for_keys_7_8_9', 'key8' => 'value_for_keys_7_8_9', 'key1' => 'value_for_keys_1_2_3', 'key9' => 'value_for_keys_7_8_9', 'key2' => 'value_for_keys_1_2_3', 'key3' => 'value_for_keys_1_2_3', 'key4' => 'value_for_keys_4_5_6', 'key5' => 'value_for_keys_4_5_6', 'key6' => 'value_for_keys_4_5_6' }; c:\test>

    Examine what is said, not who speaks.

    The 7th Rule of perl club is -- pearl clubs are easily damaged. Use a diamond club instead.

Re: Splitting an array to populate hash
by broquaint (Abbot) on Jan 29, 2003 at 11:15 UTC
    How about ...
    use Data::Dumper; my @data = ( qq{key1~key2~key3 Value for key1, key2, key3}, qq{key4~key5~key6 Value for key4, key5, key6}, qq{key7~key8~key9 Value for key7, key8, key9} ); my %hash; for(@data) { my($value, @keys) = reverse /(\w+)(?:~|$)/g, /\n(.*)/; @hash{@keys} = ($value) x @keys; } print Dumper( \%hash ); __output__ $VAR1 = { 'key7' => 'Value for key7, key8, key9', 'key8' => 'Value for key7, key8, key9', 'key9' => 'Value for key7, key8, key9', 'key1' => 'Value for key1, key2, key3', 'key2' => 'Value for key1, key2, key3', 'key3' => 'Value for key1, key2, key3', 'key4' => 'Value for key4, key5, key6', 'key5' => 'Value for key4, key5, key6', 'key6' => 'Value for key4, key5, key6' };
    And not a map() in sight!
    HTH

    _________
    broquaint

Re: Splitting an array to populate hash
by Aragorn (Curate) on Jan 29, 2003 at 10:59 UTC
    Something like this?
    my @data = ( qq{key1~key2~key3\nvalue for key1, key2, key3}, qq{key4~key5~key6\nValue for key4, key5, key6}, qq{key7~key8~key9\nValue for key7, key8, key9} ); my %hash; foreach my $d (@data) { my ($key_str, $val_str) = split("\n", $d); my @keys = split("~", $key_str); @hash{@keys} = ($val_str) x scalar(@keys); }

    This can probably be done in less code, but I think this shows the idea quite nicely.

    Arjen

Re: Splitting an array to populate hash
by Hofmator (Curate) on Jan 29, 2003 at 12:52 UTC

    I think using a hash slice is the solution to your problem, the way broquaint uses it - it's just a bit hidden in his code so I'd like to highlight it.

    Leaving out the problem of how to split out the keys and the value, this is how you assign multiple keys to one value in a hash: @hash{ @keys } = ( $value ) x @keys; In the more general version, this allows you to set multiple keys to multiple values in a hash: @hash{ @keys } = @values; The first example simply builds the array on the right-hand side on the fly by repeating the $value as many times as the length of the array @keys.

    -- Hofmator