Question

As a beginner I have what I think is a rather complicated problem I am hoping someone could help with.

I have the following text file (tab delminated)...

FILE1.txt

Dog     Big     
Dog     Medium     
Dog     Small     
Rabbit     Huge     
Rabbit     Tiny     
Rabbit     Middle    
Donkey     Massive    
Donkey     Little   
Donkey     Gigantic

I need to read FILE1.txt into a hash reference to get something like the following... (using Data::Dumper)

$VAR1 = {
        'Dog' => {
                 'Big',
                 'Medium',
                 'Small'
                 },
        'Rabbit  => {
                    'Huge',
                    'Tiny',
                    'Middle'
                    },
        'Donkey  => {
                    'Massive',
                    'Little',
                    'Gigantic'
                    },                               
        };

The problem I am having:

I then need to loop through each branch of the hash reference one at a time, I will use the value from the hash reference to check if this matches my keyword, if so it will then return it's corresponding key.... for example...

What I need it to do:

my $keyword == "Little";

Dog->Big 
if 'Big' matches my keyword then return $found = Dog
else go to the next branch
Rabbit->Huge
if 'Huge' matches my keyword then return $found = Rabbit
else go to the next branch
Donkey->Massive
if 'Massive' matches my keyword then return $found = Donkey
else go to the next branch (which is Dog again, but the second element this time)
Dog->Medium
if 'Medium' matches my keyword then return $found = Dog
else go to the next branch
Rabbit->Tiny
if 'Tiny' matches my keyword then return $found = Rabbit
else go the the next branch
Donkey->Little
if 'Little' matches my keyword then return $found = Donkey

..... and so on until the keyword is found or we reach the end of the hash reference

This is the kind of thing I am trying to achieve but don't know how to go about doing this, or whether a hash reference is the best way to do this, or if it can even be done with a hash/hash reference?

your help with this is much appreciated, thanks

Was it helpful?

Solution

Choosing proper data structure is often key step to the solution, but first of all you should define what you are trying achieve. What is overall goal? For example I have this data file and in mine application/program I need frequently ask for this information. It is crucial to ask proper question because for example if you don't need ask frequently for keyword it doesn't make sense creating hash at all.

 perl -anE'say $F[0] if $F[1] eq "Little"' FILE1.txt

Yes it is that simple. Look in perlrun manpage for switches and what they mean and how to do same thing in bigger application.

If you need frequently ask for this question you should arrange your data in way which helps you and not in way you have to battle with.

use strict;
use warnings;
use feature qw(say);
use autodie;

open my $f, '<', 'FILE1.txt';
my %h;
while(<$f>) {
    chomp;
    my ($animal, $keyword) = split' ';
    $h{$keyword} = $animal unless exists $h{$keyword};
}

close $f;

for my $keyword (qw(Little Awkward Small Tiny)) {
    say $h{$keyword} ? "$keyword $h{$keyword}" : "keyword $keyword not found";
}

But if you still insist you want to traverse hash you can do it but you has been warned.

open my $f, '<', 'FILE1.txt';
my %h;
while (<$f>) {
    chomp;
    my ( $animal, $keyword ) = split ' ';
    push @{ $h{$animal} }, $keyword;
}

close $f;

KEYWORD:
for my $keyword (qw(Little Awkward Small Tiny)) {
    for my $animal (keys %h) {
        for my $k (@{$h{$animal}}) {
            if($k eq $keyword) {
                say "$keyword $animal";
                next KEYWORD;
            }
        }
    }
    say "keyword $keyword not found";
}

OTHER TIPS

To critique my own answer: the structuring of the part that does the search could be better. And maybe it is pointless even using an ordered hash as the search is through a linear list. Maybe it should be an array of arrays

   use strict;
    use warnings;
    use Tie::IxHash;
    #open file
    open(my $fh,"ani.txt") ||die $!;

    #make an ordered hash
    tie my %sizes, 'Tie::IxHash';


    #read file into hash of arrays
    while(<$fh>) {
       (my $animal,my $size)=split(/\s+/);
       if (!exists($sizes{$animal})) {
           $sizes{$animal} = [$size];
       } else { 
           push @{$sizes{$animal}},$size;
       }
    }

    my $keyword="Little";
    my $running=1;
    my $depth=0;
    while( $running ) {
      $running = 0;
      for my $search (keys %sizes) {
          next if ($depth > @{$sizes{$search}});
          $running = 1;
          if ($keyword eq $sizes{$search}[$depth]) {
              print "FOUND!!!!!! $search $depth";
              exit(0);
          }
      }
      $depth++;
    }

Here is another version of a solution to the stated problem. To solve the actual problem given there is no need to store anything except the first "size" key for each animal in a hash

This hash can then be trivally used to look up the animal

use strict;
use warnings;
open(my $fh,"ani.txt") ||die $!;

my %animals;

#read file into hash
while(<$fh>) {
   (my $animal,my $size)=split(/\s+/);
   #only add the animal the first time the size is found
   if (!exists($animals{$size})) {
       $animals{$size} = $animal;
   } 
}

my $keyword="Little";
print "animal is ", $animals{$keyword};
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top