简体   繁体   中英

How to iterate over an array of hashes in an array of hashes

Lots of threads about iterating over an array of hashes, which I do daily. However now I want to iterate over an AoH in and AoH. I'm interested in the array "chapters" because I want to add a hash to each array element of that inner array:

$criteria = [
    {
      'title' => 'Focus on Learning',
      'chapters' => [
                          {
                            'content_id' => '182',
                            'criteria_id' => '1',
                            'title' => 'Focus on Learning',
                          },
                          {
                            'content_id' => '185',
                            'criteria_id' => '1',
                            'title' => 'Teachers Role',
                          },
                          {
                            'content_id' => '184',
                            'criteria_id' => '1',
                            'title' => 'Parents in Class',
                          },
                          {
                            'content_id' => '183',
                            'criteria_id' => '1',
                            'title' => 'Students at Home',
                          }
                        ],
      'tot_chaps' => '4'
    },

This, in theory, is want I want to do.

for my $i ( 0 .. $#$criteria ) {
   for my $j ( 0 .. $#$criteria->[$i]{'chapters'}) {
      print $criteria->[$i]{'chapters'}->[$j]{'title'}."\n";
   }
}

print $criteria->[$i]{'chapters'}->[1]{'title'}."\n"; -> Teachers Role
use warnings;
use strict;

foreach my $criterion (@$criteria) {
  foreach my $chapter (@{$criterion->{chapters}}) {
    print $chapter->{title}, "\n";
  }
}

I agree with perreal, but to answer your question a bit more narrowly — your code should be fine, except that, to use the $#$arrayref notation when $arrayref is no longer a variable, you use curly brackets {} so that Perl can tell the extent of the reference:

   for my $j ( 0 .. $#{$criteria->[$i]{'chapters'}}) {

(Even when $arrayref is a variable, you can use curly brackets to be explicit:

for my $i ( 0 .. $#{$criteria} ) {

. And the same applies to other ways of dereferencing: @$arrayref or @{$arrayref} , %$hashref or %{$hashref} , and so on.)

It is often easier to iterate over data structures like this rather than get the indicies of all of the levels (like your code was doing). This lets you handle each level by itself:

for my $criterion (@$criteria) {
    print "$criterion->{title}\n";
    for my $chapter (@{ $criterion->{chapters} }) {
        print "\t$chapter->{title}\n";
    }
}

As a side note, the tot_chaps key is dangerous if this is not a read-only data structure. It is very easy for it to get out of sync with $criterion->{chapters} and duplicates the information already stored in the array:

my $total_chapters = @{ $criterion->{chapters} };

If you want to add something, just do it. I'm not sure I understood where you want to add what exactly. If you want another key/value pair for each of the chapters, do it like this:

for my $i ( 0 .. $#$criteria ) {
  for my $j ( 0 .. $#{$criteria->[$i]{'chapters'}}) {
    $criteria->[$i]{'chapters'}->[$j]->{'Teachers Role'} = 'Stuff';
  }
}

Also, there was a little bug in the code: use $#{$criteria->[$i]{'chapters'}} instead of $#$criteria->[$i]{'chapters'} because the $# part only works until the first -> , so it tries to access ->[$i]{'chapters'} out of the value of $#$criteria which is a number and doesn't work.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM