Sandra Schlichting
Sandra Schlichting

Reputation: 26046

How to loop over an Array of Arrays as reference?

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;

Upvotes: 7

Views: 20429

Answers (6)

TLP
TLP

Reputation: 67910

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;

Upvotes: 4

David W.
David W.

Reputation: 107090

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};
}

Upvotes: 1

Bill Ruppert
Bill Ruppert

Reputation: 9026

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;
    }
}

Upvotes: 1

DVK
DVK

Reputation: 129529

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)

Upvotes: 18

dan1111
dan1111

Reputation: 6566

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;
        }
    }
}

Upvotes: 4

cdtits
cdtits

Reputation: 1128

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

Upvotes: 0

Related Questions