7

The below code works if $a is an Array of Arrays, but I need $a to be an reference to the Array of Arrays.

Question

How do I iterate through $a?

#!/usr/bin/perl

use warnings;
use strict;
use Data::Dumper;

my @AoA = ( ['aaa','hdr','500'],
            ['bbb','jid','424'],
            ['ccc','rde','402'],
            );

my $a = \@AoA;

my $s = "bbb";
my $d = "ddd";

for my $i ( 0 .. $#a ) {
    for my $j ( 0 .. $#{ $a[$i] } ) {
        if ($a[$i][$j] eq $s) {
            $a[$i][$j] = $d;
            last;
        }
    }
}

print Dumper $a;
1
  • 2
    good question! I was actually wondering about the same thing. Commented Apr 5, 2013 at 19:44

6 Answers 6

18
foreach my $row (@$array_ref) {
    foreach my $cell (@$row) {
        if ($cell eq $s) {
            $cell = $d;
            last;
        }
    }
}

Also, to compute the # of elements in array reference (which as you can see from above code you don't need for your specific code), the easiest approach is:

my $count = scalar(@$array_ref);
my $row_count = scalar(@{ $array_ref->[$i] });
my $last_index = $#$array_ref; 

Also, to access the data inside an arrayref of arrayrefs, you simply use the dereference operator on it:

$array_ref->[$i]->[$j]; # The second arrow is optional but I hate omitting it.

In addition, you can create your arrayref of arrayrefs the way you did (take a reference to array of arrays), or right away as a reference:

my $array_ref = [
         [1,2,3]
         ,[4,5,6]
        ];

As a side note, please never use $a and $b as identifyer names - they have special purpose (used in sort blocks for example)

Sign up to request clarification or add additional context in comments.

4 Comments

$#$a also works and gives the last array index, which is what was needed in this case. Ugly? Perhaps, but scalar(@$a) isn't exactly intuitive, either.
@dan - yeah, I included the $#$ operation for completeness. It's not so much ugly, but less readable and definitely less useful. I very rarely have to do C style loops over index increment in Perl.
I agree that C-Style loops should be avoided but for (0..$#array) {} is often useful.
+1 for using array refs instead of indexes. IMO indexes should only be used when that particular meta info is required for some other purpose.
4

To dereference in Perl, you have a variety of options. For starters, I suggest you read perlref. Here is the relevant code portion with a minimum of changes, so you can see what needs to be different (however, I agree with others' suggestions to make your code more Perlish).

for my $i ( 0 .. $#$a ) {
    for my $j ( 0 .. $#{ $a->[$i] } ) {
        if ($a->[$i][$j] eq $s) {
            $a->[$i][$j] = $d;
            last;
        }
    }
}

Comments

4

The easiest way to dereference, in my opinion, is to always remember that arrays and hashes can only ever contain scalar values (see perldoc perldata). Which means that your array

my @AoA = ( ['aaa','hdr','500'],
            ['bbb','jid','424'],
            ['ccc','rde','402']);

...contains only three scalar values, which are references to other arrays. You could write it like this:

my $first = [ qw(aaa hdr 500) ];      # using qw() which will quote the args
my $sec   = [ qw(bbb jid 424) ];
my $third = [ qw(ccc rde 402) ];
my @all   = ($first, $sec, $third);   # this array is now identical to @AoA

With this in mind, it is simple to imagine (like DVK suggested in his answer) a loop such as:

for my $aref (@all) {
    # $aref will contain $first, $sec, and $third 
}

And knowing that $aref is an array reference, the dereferencing is simple:

for my $aref (@all) {
    for my $value (@$aref) {
        # do your processing here
    }
}

Also, since the values in a for loop are aliased, any changes to them affects the original array. So if the "processing" in the loop above contains an assignment such as

$value = $foo;

That means that the values in @all are also changed, exactly as if you had written:

$all[0][1] = $foo;

Comments

1

I found the use of "last" to be ambiguous, inner or outer, so I made it explicit.

for (@$a) {
    INNER:
    for (@$_) {
        do {$_ = $d; last INNER} if $_ eq $s;
    }
}

Comments

1

Whenever I deal with arrays of arrays or hashes or hashes or arrays of hashes of arrays, I start to think in terms of Object Oriented programming. Perl OOP isn't all that complex, and it hides a lot of the logical complexity.

Now, in this very simple example, it would be a lot easier just to write it without object orientation, and you already have several good answers (you mainly forgot to dereference your references).

But once you start realizing that you can have more complex structures in Perl than simple hashes, arrays, and scalars, your data structures start getting more complex and more difficult to parse without OOP.

#!/usr/bin/env perl

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

my $a_of_a = array_of_arrays->new;

$a_of_a->push(["aaa", "hdr", "500"]);
$a_of_a->push(["bbb", "jid", "424"]);
$a_of_a->push(["ccc", "rde", "402"]);

foreach my $member ($a_of_a->list) {
    my @array = @{$member};
    foreach my $element (@array) {
        printf "%-5.5s  ", $element;
    }
    print "\n";
}


package array_of_arrays;

sub new {
    my $class = shift;
    my $self = [];

    bless $self, $class;
    return $self;
}

sub push {
    my $self = shift;
    my $item = shift;

    push @{$self}, $item;
    return $self;
}

sub pop {
    my $self = shift;

    if (scalar @{$self}) {
        return pop @{$self};
    }
    else {
        return;
    }
}

sub list {
    my $self = shift;
    return @{$self};
}

Comments

0
for my $x (@{$a}) {
    for (@{$x}) {
        if ($_ eq $s) {
            $_ = $d;
            last;
        }
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.