yonetpkbji
yonetpkbji

Reputation: 1019

Perl - Printing elements within a hash structure

I have a huge, huge, huge data structure, exactly in the format as shown below from Data::Dumper (though greatly simplified for the purposes of explaining the problem).

{
  Fred => {
            "Street Name" => ["First Avenue"],
            "animal" => ["lion", "snake", "spider", "monkey"],
          },
  Dave => {
            "Street Name" => ["Church Street"],
            "animal" => ["dog", "cat", "pig", "elephant"],
          },
}

I am having real issues trying to access the data from further down this hash structure, something that I have done multiple times previously, but it is not working in this instance from some reason.

What is the correct method to access each of the elements in this hash structure and print each level of the structure? e.g.

   foreach my $key ( keys %hashStructure ) {
          print "$key";
          foreach my $key2 ...

Upvotes: 0

Views: 345

Answers (2)

David W.
David W.

Reputation: 107080

Here's your structure:

{
  Fred => {
            "Street Name" => ["First Avenue"],
            "animal" => ["lion", "snake", "spider", "monkey"],
          },
  Dave => {
            "Street Name" => ["Church Street"],
            "animal" => ["dog", "cat", "pig", "elephant"],
          },
}

Let's take this apart one level at a time. There's three levels here:

The outer level represents a hash I'll call %person_hash. Your hash has two keys in it: Fred and Dave. Each of the values of the these two hashes point (reference) other hashes. That is $person_hash{Dave} is a hash reference and $person_hash{Fred} is a hash reference.

To turn those two hash references into hashes, I use the dereferencing syntax:

%attribute_hash = %{ $person_hash{Dave} };

Now, we have a hash called %attribute_hash. This %attribute_hash contains attributes for Fred and Dave. In your example, there are two elements in each of these %attribute_hash hashes (Remember: There's one for Dave and one for Fred). The two keyed elements in these %attribute_hash hashes contain "Street Address" and "animal".

To access the lists, I can use the dereferencing syntax: @values = @{ $attribute_hash{$attribute} }.

So, let's look at how we can print all of this out:

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

my %person_hash = (
    Fred => {
        "Street Name" => [ "First Avenue" ],
        animal => [ "lion", "snake", "spider", "monkey" ],
    },
    Dave => {
        "Street name" => [ "Church Street" ],
            animal =>  [ "dog", "cat", "pig", "elephant" ],
    },
);

# The first level contains the keys `Dave` and `Fred`
for my $person ( keys %person_hash ) {
    say "The person is $person";

    # The keys will be for "Dave" and "Fred", and will be the value
    # of $person. This is a hash reference, so let's dereference it.
    my %attribute_hash = %{ $person_hash{$person} };

    # We have a hash of attributes beloning to that person. The
    # attributes will be "Street Name" and "animal"
    for my $attribute ( keys %attribute_hash ) {
        say "    ${person}'s attribute is '$attribute'";

        # Each "attribute" points to a list. Let's get the list
        my @value_list = @{ $attribute_hash{$attribute} };

        # Now we can go through that list:
        for my $value ( @value_list ) {
            say "        ${person}'s attribute '$attribute' has a value of $value";
        }
    }
}

This prints out:

The person is Dave
    Dave's attribute is 'Street name'
        Dave's attribute 'Street name' has a value of Church Street
    Dave's attribute is 'animal'
        Dave's attribute 'animal' has a value of dog
        Dave's attribute 'animal' has a value of cat
        Dave's attribute 'animal' has a value of pig
        Dave's attribute 'animal' has a value of elephant
The person is Fred
    Fred's attribute is 'Street Name'
        Fred's attribute 'Street Name' has a value of First Avenue
    Fred's attribute is 'animal'
        Fred's attribute 'animal' has a value of lion
        Fred's attribute 'animal' has a value of snake
        Fred's attribute 'animal' has a value of spider
        Fred's attribute 'animal' has a value of monkey

You should also know that I can access the values directly inside these by using the -> syntax:

say "Fred's first animal in his list is " . $person_hash{Fred}->{animal}->[0];

I can also use that syntax when I dereference:

say "Fred's animals are " . join ", ", @{ $person_hash->{Fred}->{animal} };

Note that $person_hash->{Fred}->{animal} is a reference to the array that contains the animals.

Upvotes: 4

Hunter McMillen
Hunter McMillen

Reputation: 61540

You just have to think about what the data at each level is. It might be worthwhile to encapsulate this data which appears to be about a person using a Person class, this would greatly simplify your code to print out these values.

#!/usr/bin/perl

use strict;
use warnings;

my %hash =  (
   'Fred' => {
      'Street Name' => ['First Avenue'],
      'animal'      => ['lion','snake','spider','monkey',]
   },
   'Dave' => {
      'Street Name' => ['Church Street'],
      'animal' => ['dog','cat','pig','elephant',]
   }
);

foreach my $namekey ( keys %hash ) {
   print "Name: $namekey\n";
   foreach my $key ( keys %{$hash{$namekey}} ) {
      print "$key: " .
         join(',', @{$hash{$namekey}{$key}}) . "\n";
   }
}

__END__ # outputName: Dave
animal: dog,cat,pig,elephant
Street Name: Church Street
Name: Fred
Street Name: First Avenue
animal: lion,snake,spider,monkey

Person example:

package Person; 

sub new {
   my ($class, %args) = @_; 

   bless \%args, $class;
}

sub name {
   my $self = shift;

   return $self->{name};
}

sub street_name {
   my $self = shift;

   return $self->{'Street Name'};
}

sub animal {
   my $self = shift; 

   return $self->{animal};
}

sub as_string {
   my $self = shift;

   return join("\n", 
      $self->name, 
      join(',', @{$self->street_name}),
      join(',', @{$self->animal})
   );
}

1;

my $fred = Person->new(
   name          => 'Fred',
   'Street Name' => ['First Avenue'],
   animal        => ['lion','snake','spider','monkey',]
);
print $fred->as_string . "\n";

__END__ # output
Fred
First Avenue
lion,snake,spider,monkey

Upvotes: 1

Related Questions