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

First off I apoligize if this has been asked before, but I couldn't find a post that met my needs. I'm pretty new to perl, so I think this is a fairly simple question. I have a loop that goes over a file, and parses out the username, and the project that user works on from the file. I then want to put the project name as a key into a hash, and then have an array of users that is associated to that key. I am currently trying to do that by:
1. foreach (@ufile_contents) 2. { 3. ($buser2,$email,$project) = split(/\|/,$_); 4. if($uo_hash{lc($buser2)} =~ /WO/) 5. { 6. @usr_list = $wo_usr_proj{$project}; 7. push(@usr_list,$buser2); 8. $wo_usr_proj{$project} = @usr_list; 9. } 10. }
the if statement just makes sure that the user belongs to a certain office before it gets put into the hash. the hash is wo_usr_proj, and I want to use @usr_list for the hash value. when I check what @usr_list is set to after line 6 I see that its just an empty array, so when I push something on to it, I'm getting a two item array. this is true through out the whole execution of the code, not just on the first step. Also, shouldn't it be a single item array after the push since the initial array was empty. I'm getting an empty slot and then the username when I print out the array. Any ideas on what I'm doing wrong? thanks in advance.

Replies are listed 'Best First'.
Re: dealing with arrays as hash values
by Zaxo (Archbishop) on May 17, 2004 at 19:56 UTC

    You're not that far off, I think your code would work if you replaced line 8 with $wo_usr_proj{$project} = \@usr_list; - hash values must be scalars, so a reference is needed.

    Your code can be cleaned up a bit. You can read data directly from the file, for (<INFH>) { Loop variables are better as lexicals, and split uses $_ as the default second argument,     my ($user, undef, $project) = split /\|/; The remainder can be condensed to a conditional push onto the dereferenced array in the hash,

    push @{$wo_usr_proj{$project}}, $user if $uo_hash{lc($user)} =~ /WO/; }

    After Compline,
    Zaxo

      Thanks, your comments for cleaning up my code helped a lot. I switched over that entire three lines , 6-8, to
      push @{$wo_usr_proj{$project}}, $user;
      I had some other stuff in that if statement that needed to be done too. but now when I print out the values from %wo_usr_proj, I only get array memory addresses:
      foreach $proj(sort(keys(%wo_usr_proj))) { print RHANDLE "$proj:\n"; foreach $element(sort($wo_usr_proj{$proj})) { print RHANDLE "$element\n"; } print RHANDLE "\n\n"; }
      That gave me the array memory adresses as output into my file I then tried:
      foreach $proj(sort(keys(%wo_usr_proj))) { print RHANDLE "$proj:\n"; foreach $element(sort($wo_usr_proj{$proj})) { print RHANDLE \$element; print RHANDLE "\n"; } print RHANDLE "\n\n"; }
      thinking that by derefencing $element it would give me the value, but instead it gave me scaler memory adresses. I don't think that the user names are actually getting put into the hash. Is there some initialization that needs to occur to "tell" the hash that its value is an array? I think if I get past this last part I'll be done. Thank you in advance for any help or comments on my code.
        In your first code above, $wo_usr_proj{$proj} is a reference to an array, not an array. You need to dereference this value, as you did in your call to push:
        push @{$wo_usr_proj{$project}}, $user;
        Currently the sort call is only sorting a list with one value (the address of the array stored in $wo_usr_proj{$proj}). The code below corrects this by dereferencing the hash element to retrieve the array:
        foreach $proj(sort(keys(%wo_usr_proj))) { print RHANDLE "$proj:\n";$wo_usr_proj{$proj} # note dereference: @{ $array_ref_here } foreach $element(sort @{ $wo_usr_proj{$proj} }) { print RHANDLE "$element\n"; } print RHANDLE "\n\n"; }
        Please take a look at Data::Dumper, as it is invaluable in illustrating the structure of the data in your code. You simply pass the Dumper function a reference to the data structure you wish to see expanded:
        use Data::Dumper; print Dumper \%wo_usr_proj; __END__ $VAR1 = { 'project2' => [ 'WO', 'WO2 ], 'project1' => [ 'WO' ] };
        As for your second code snippet, the code is not dereferencing $element, but rather, is creating another reference to $element.

        If $scalar is a reference to a scalar value, dereference using $$scalar:
        my $foo= "bar"; my $ref= \$foo; print $$ref; __END__ bar
        If $arr is a reference to an array, dereference using @$arr:
        my @array= qw(foo bar baz); my $ref= \@array; print join ':' => @$ref; __END__ foo:bar:baz
        If $href is a reference to a hash, dereference using %href:
        my %h= ( foo => 'bar' ); my $ref= \%h; while( my( $k, $v )= each %$ref ) { print "$k -> $v" } __END__ foo -> bar
        Please see the pod for perlreftut, perldsc, and perllol for more details.

        --sacked