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

Hi PerlMonks:

I have an array of hashes. I am interested in knowing whether there is a way to find the array subscript by specifying one of the hash key?
Please check the following sample code.
My Code
use strict; use warnings; use Data::Dumper; my @outputFiles; my $count = 1; setOutputFiles('captureID1','C:/temp/test.xml',4); getOutputFiles('captureID1'); sub setOutputFiles { my $captureId = shift; my $outputFile = shift; my $fileType = shift; $outputFiles[$count]{$captureId} = $outputFile; $outputFiles[$count]{'type'} = $fileType; print Dumper(\@outputFiles); } sub getOutputFiles { my $captureId = shift; my $count; # loop which will fetch me the $count of array @outputFiles # having the hash key $captureId. # End of Loop print $outputFiles[$count]{$captureId}; print "\n"; print $outputFiles[$count]{'type'}; }

Replies are listed 'Best First'.
Re: Array of Hashes
by japhy (Canon) on Sep 20, 2005 at 17:31 UTC
    You'll have to loop over the array, like so:
    my $wanted; for my $href (@outputFiles) { $wanted = $href, last if exists $href->{$captureId}; }
    My code gives you $wanted which IS the hash reference, rather than the index in the array. You'd print $wanted->{$captureId} and $wanted->{type}.

    If you don't have duplicate hash keys, though, why are you using an array of hash refs?


    Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
    How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
      Thanks that works!

      <If you don't have duplicate hash keys, though, why are you using an array of hash refs?>
      I will not have duplicate captureID's. But I want to record the data in the following table using some Perl data structure.

      captureID | outputFileLocation| Filetype
      ------------|--------------------|---------
      captureID1 | c:/temp/test.xml | 4
      captureID2 | c:/test/temp.xml | 6 ......

      In this captureID is unique. Is there any other way of handling this data other than array of hashes?
        Is there any other way of handling this data...

        Since all the "captureID" values are unique, and these are the values that you want to use for fetching information about a given entry, I guess the question is, "why use an array at all?" I think a HoH (or even a simple hash) would work just as well:

        my %outputFiles; sub setOutputFile { my ($id,$path,$type) = @_; # HoH method: $outputFiles{$id}{path} = $path; $outputFiles{$id}{type} = $type; # or, simple hash method: # $outputFiles{$id} = join "=:=", $type, $path; } sub getOutputFiles { my $id = shift; # HoH method: my $type = $outputFiles{$id}{type}; my $path = $outputFiles{$id}{path}; # or, simple hash method: # my ( $type, $path ) = split /=:=/, $outputFiles{$id}; # ... do something with $path and $type ... }
      But though it is less efficient?

      Could you not keep a second index. Or multiple indicies? Or is there a better way. I am asking since I am holding data from a database in a loh for html::template's happiness and the second index prevents a linear scan each time.

      %primary_key{ $capture_id } = $array_index;
Re: Array of Hashes
by friedo (Prior) on Sep 20, 2005 at 17:33 UTC
    It's easy to get the element (or elements, since there can be more than one) directly by using grep.

    my @elements = grep { exists $_->{$captureId} } @outputFiles;

    If you really want the index of the element, rather than the element itself, you can change the grep to:

    my @indexes = grep { exists $outputFiles[$_]{$captureId} } 0..$#outputFiles;
      Thanks friedo. But I am getting the following error when trying to run your code.
      "Modification of a read-only value attempted at" the following line.

      my @elements = grep { exists $_->{$captureId} } @outputFiles;
        I don't get that error. Are you certain that what you have is an array of hashes? Here is a one-liner which shows that it works:

        perl -Mstrict -Mwarnings -MData::Dumper -e 'my @array = ( { foo => 1 }, { }, { }, { foo => 2 } ); my @elements = grep { exists $_->{foo} } @array; print Dumper \@elements' $VAR1 = [ { 'foo' => 1 }, { 'foo' => 2 } ];
Re: Array of Hashes
by graff (Chancellor) on Sep 20, 2005 at 17:48 UTC
    Now that friedo japhy has given you the answer you want, let me suggest that your "setOutputFiles" sub should be using "push" rather than referencing a global-scope array index variable (especially since, in the code you posted, it looks like you might forget to increment the global instance of $count). How about:
    sub setOutputFiles { my ($captureId,$outputFile,$fileType) = @_; push @outputFiles, { $captureId => $outputFile, 'type' => $fileType }; print Dumper(\@outputFiles); }
    Either that, or else you'll want to pass an array index value in the call to this sub (e.g. in case you want to assign different values to an existing array element).