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

Hi, Is it possible to create different hash for multiple files in the context of:
for ($i=1; $i<=$number; $i++) { open (lookup, "<file" . "$i"); while (<lookup>) { ($ref_name, $ref +_id) = (split /\t/, $_)[3,4] $hash_name{$ref_id} = $ref_name; } }
where the hash name is hash_name1 hash_name2, dependent on the value of $i. Basically i need to create hash lookups for multiple files which will vary each time Thanks

Replies are listed 'Best First'.
Re: multiple files and hashed
by davido (Cardinal) on Nov 06, 2006 at 16:34 UTC

    Why would you want to do that when Perl offers arrays of hashes? In the POD, the Perl Datastructure Cookbook ought to get you going along those lines. Imagine a datastructure that looks like this (pseudocode):

    @lookup = ( { id => value, another_id => value, }, { id => value, #etc } );

    With that structure, you could access the inner hashes like this:

    my $value = $lookup[0]->{id};

    What you're asking can be done, but should not be done. You don't have one of those extremely rare special cases that justifies using symbolic references. Real references are a better way to accomplish the task you're trying to achieve.


    Dave

Re: multiple files and hashed
by davorg (Chancellor) on Nov 06, 2006 at 16:36 UTC

    You're looking for symbolic references (aka "getting a variable name from a variable"). It's rarely a good idea to use symbolic references in a Perl program - which is why they are one of the features that "use strict" disables.

    It looks like what you want here is an array of hashes.

    my @data; for ($i=1; $i<=$number; $i++) { open (LOOKUP, '<', "file$i"); while (<LOOKUP>) { chomp; my ($ref_name, $ref_id) = (split /\t/, $_)[3,4]; $data[$i]{$ref_id} = $ref_name; } }

    See perldsc for more details on building Perl data structures.

    --
    <http://dave.org.uk>

    "The first rule of Perl club is you do not talk about Perl club."
    -- Chip Salzenberg

Re: multiple files and hashed
by grep (Monsignor) on Nov 06, 2006 at 16:43 UTC
    You wouldn't want to do that. That would involve symbolic references, and if you're asking the question 'What is a symbolic reference?' you definitely should not be using them. In fact there is almost never a reason to use them.

    What you want is a HoH (Hash of Hashes).

    %hash = { filename1 => { id1 => 1, id2 => 1 }, filename2 => { id1 => 3, id2 => 4 } };

    like this:

    use strict; use warnings; #NOT TESTED my %filehash; for ($i=1; $i<=$number; $i++) { my $filename = "file$i"; open (lookup, "<$filename") or die "Can't open $filename : $!\n"; while (<lookup>) { ($ref_name, $ref_id) = (split /\t/, $_)[3,4]; $filehash{$filename}{$ref_id} = $ref_name; } }


    grep
    One dead unjugged rabbit fish later
      thanks for all your replies and help pointing me in the right direction
Re: multiple files and hashed
by jbert (Priest) on Nov 06, 2006 at 17:14 UTC
    On a side note, you rarely really want a C-style for(;;) loop in perl.

    In your case it looks as though you want to access files called file1, file2, etc in turn.

    A more perlish way of doing this (and one which is perhaps more robust against changes in the future) is to work out a list of filenames and process each of them.

    You can use the glob function to get a list of files matching a pattern and then process each of those filenames. The pattern format isn't a regexp, but the same sort of pattern you'd give to a shell in Unix. For example, if you wanted to process every file in the current directory beginning with foo you could do:

    my @fnames = glob("foo*"); foreach my $fname (@fnames) { # Do something then $fname... }
    If you want to use more complicated patterns, or prefer regexps, you can always just get all the names into a list and use grep:
    my @fnames = grep { /f(oo|u|)[0-9]{2}\.txt/ } glob("*");
Re: multiple files and hashed
by johngg (Canon) on Nov 06, 2006 at 16:58 UTC
    If I understand what you are trying to do, you probably need a single HoH (hash of hashes) structure rather than several different hashes. Something along the lines of

    use strict; use warnings; my @filesThisRun = (qw{file1 file2 ... fileN); my %fileInfo = (); foreach my $file (@filesThisRun) { open my $lookupFH, q{<}, $file or die qq{open: $file: $!\n}; while (<$lookupFH>) { my ($refName, $refID) = (split m{\t})[3, 4]; $fileInfo{$file} = {$refID => $refName}; } close $lookupFH or die qq{close: $file: $!\n}; }

    That gives you a hash keyed by file name with the value for each file being an anonymous hash of refID/refName pairs. If you need the count number rather than the file name as the key then increment a count each time through the foreach and use that as the key instead of the file name. To access the refName for file "file2", refID "XX" you would do

    my $queryFile = q{file2}; my $queryRefID = q{XX}; my $resRefName = $fileInfo{$queryFile}->{$queryRefID};

    I hope this is of use.

    Cheers,

    JohnGG

Re: multiple files and hashed
by suaveant (Parson) on Nov 06, 2006 at 16:36 UTC
    You can assign a hash ref to a glob to create a hash in the local namespace...
    my $name = 'foo'; *{$name} = { a => 1, b => 2}; print "$foo{b}\n";
    or you could even use eval...

    I would suggest not doing either and just using a multilevel hash, with the first key being the name you wanted the hash to be, so... $big_hash{$hash_name}{$ref_id} if you can do this your life will be much easier, though I realize there are reason sometimes you want specific veriable names defined. Avoid if possible.

    You may need a no strict; around it if you are using strict.

    Update I just thought you could actually have the best of both worlds (i.e. not doing namespace mangling AND having different variable names) if you have a finite number of variables. Then you could create the variables, store references to them in a hash, then assign values to the refs, as so...

    my(%h1,%h2,%h3); my %lookup = ( 1 => \%h1, foo => \%h2, c => \%h3 ); $which_hash = 'foo'; $lookup->{$which_hash}{test} = 'value'; $h2{test} eq 'value';
    much cleaner. Though having a hash of hashes or an array of hashes is in some ways better, giving you the ability to iterate through them all, not to mention just all around cleaner.

                    - Ant
                    - Some of my best work - (1 2 3)