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

I'm new to Perl and have hit a brick wall trying to make an array of references to hashes. My hosted server has Perl 5.8.8 installed. Here's the relavent part of my code, stripped down. I'm trying to find all files in a directory and for each call the filer function which should make a hash with some information about the file and push a reference to that hash unto an array

@files = (); sub filer { $filepath_rs = $File::Find::name; if(-f $filepath_rs) { $metadata = {}; #should instantiate a new hash object and retu +rn a scalar reference. ${metadata}{'path'} = $filepath_rs; print ${metadata}{'path'}; #prints out fine push(@files, $metadata); #should add reference to hash to arra +y. } } find(\&filer, $mydir); foreach(@files) { print ${$_}{'path'}; #nothing prints out }

Replies are listed 'Best First'.
Re: Array of Hashes
by roboticus (Chancellor) on Oct 28, 2011 at 15:47 UTC

    tobeythorn:

    Here's a simple example:

    $ cat foo.pl use strict; use warnings; my @AoH; # Build the AoH while (<DATA>) { chomp; last if /^\s*$/; my ($file, $path) = split /:/; push @AoH, { path=>$path, file=>$file }; } # Print the data for (@AoH) { print "PATH:", $$_{path}, ", FILE:", $$_{file}, "\n"; } __DATA__ baz:/foo/bar gunk:/tmp/roboticus $ perl foo.pl PATH:/foo/bar, FILE:baz PATH:/tmp/roboticus, FILE:gunk

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

Re: Array of Hashes
by aaron_baugher (Curate) on Oct 28, 2011 at 18:02 UTC

    The problem is that ${metadata}{'path'} (which is the same thing as $metadata{path}, since curly brackets around the variable name only serve to disambiguate when it's not clear where the variable name ends), represents a value in the hash %metadata, not a value in a hash pointed to by the scalar reference $metadata. If you were using strict, it would have complained there because %metadata doesn't exist. Presumably you aren't using strict, so it happily creates %metadata on the fly, puts your keys and values in it, and then your other hash that $metadata points to is empty when you push that onto @files.

    To access the values in a hash pointed to by a scalar reference, use this format: $metadata->{path}

Re: Array of Hashes
by johnny_carlos (Scribe) on Oct 28, 2011 at 16:12 UTC
    FYI, for references, I find the alternate notation easier to read: ->{ }

    Like this: $metadata->{path} = $filepath_rs;

Re: Array of Hashes
by ~~David~~ (Hermit) on Oct 28, 2011 at 16:55 UTC
    What happens when you make everything look pretty:
    @files = (); sub filer { $filepath_rs = $File::Find::name; if(-f $filepath_rs) { $metadata = {}; #should instantiate a new hash object and retu +rn a scalar reference. $metadata->{path} = $filepath_rs; print $metadata->{path}; #prints out fine push @files, $metadata; #should add reference to hash to array +. } } find(\&filer, $mydir); foreach my $file (@files) { print $file->{path}; #??? }
Re: Array of Hashes
by tobeythorn (Initiate) on Oct 28, 2011 at 19:02 UTC

    Thank you all. The -> notation is great, and as a c programmer, makes me feel at home. I've turned strict on and it now works. Unfortunately, I am currently blind because some modrewrite in some .htaccess file is preventing me from seeing debug info.

    Perhaps you can tell me why, using the -> notation, keys do not go in quotes? In the following, what is the type of mykey? It doesn't have quotes, nor a type prefix such as $, %, or @.

    $myhash_ref->{mykey};

      why, using the -> notation, keys do not go in quotes?

      They do -- the quotes are just optional. You might need the quotes if the key name collides with a built-in function such as 'shift'

      In the following, what is the type of mykey? $myhash_ref->{mykey};

      That would be equal to the string 'mykey'

        Thanks vroom. I will be explicit then and use the quotes.
Re: Array of Hashes
by Marshall (Canon) on Oct 29, 2011 at 11:49 UTC
    The easiest way is to just push a reference to an anon hash onto the @files array as shown below...
    #!/usr/bin/perl -w use strict; use File::Find; use Data::Dumper; my @files = (); #will be an Array of Hashes my $odd =1; sub filer { my $filepath_rs = $File::Find::name; return unless -f $filepath_rs; push(@files, {file=>$filepath_rs, OddEven => $odd}); $odd ^= 1; } find(\&filer, 'C:/temp'); print Dumper \@files;
    This does the same thing:
    #!/usr/bin/perl -w use strict; use File::Find; use Data::Dumper; my @files = (); #will be an Array of Hashes my $odd =1; sub filer { my $filepath_rs = $File::Find::name; return unless -f $filepath_rs; my %metadata; $metadata{file} = $filepath_rs; $metadata{OddEven} = $odd; push(@files, \%metadata); $odd ^= 1; } find(\&filer, 'C:/temp'); print Dumper \@files;
    Every time the subroutine filer is entered a new hash %metadata is created. If a reference to some previously created %metadata hash exists, then new memory is allocated for this instance of %metadata. If %metadata is a multi-dimensional hash, the same rules apply and a complete new hash and memory allocation will be made.