0

How can I loop through an array of arrays like the following which has elements that are not references? I get the error: "Can't use string ("10") as an ARRAY ref while "strict refs" in use" but if i take out the elements '10' and '11' it prints fine.

my @array = (
    [1, 2, 3, 4, 5],
    ['x', 'y', 'z'],
    10,
    11
);

foreach my $x (@array) {
   for my $i (0..@$x) {
      if (! @$x[$i] eq '') {
          print "@$x[$i]\n";
      }
   }
}
2
  • 1
    Kudos to you for using use strict; and use warnings; in EVERY script. Glad it was able to help you find this error and therefore know to ask for help. Commented May 22, 2014 at 1:51
  • 1
    Is "AoA" supposed to be "array of arrays"? I've never heard that term before. Commented May 22, 2014 at 4:32

4 Answers 4

4
for my $x (@array) {
   # plain scalar, print it and skip to next element
   if (!ref($x)) { print "$x\n"; next; }

   for my $i (@$x) {
      print "$i\n";
   }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, didn't know about 'ref'.
2

This line:

for my $i (0..@$x) {

is where your problem currently lies. For each value in @array it attempts to place it in Array context via the @ sigil. If your element is not a reference to an Array, this will throw the error you are seeing.

What you can do is check if you are looking at an arrayref or a scalar before your inner loop begins:

ITER:
foreach my $x (@array) {
   if (not ref($x)) {
      print "$x\n";
      next ITER;
   }

   for my $i (0..@$x) {
      if (! @$x[$i] eq '') {
         print "@$x[$i]\n";
      }
   }
}

Comments

1

You can use he ref function to decide how to print an element of your array. It returns ARRAY if its parameter is an array reference, or the null string if it is a simple string or numeric value.

This program demonstrates

my @array = (
    [1, 2, 3, 4, 5],
    ['x', 'y', 'z'],
    10,
    11
);

for my $item (@array) {
  if (ref $item) {
    print "@$item\n";
  }
  else {
    print "$item\n";
  }
}

output

1 2 3 4 5
x y z
10
11

The body of the loop can be made much more concise using the conditional operator. This code is equivalent

print ref($_) ? "@$_\n" : "$_\n" for @array;

Comments

0

Another way to do this is convert anything that's a scalar into an array reference. This avoids code duplication (and the risk of error that that entails).

For example, take your array:

my @array = (
    [1, 2, 3, 4, 5],
    ['x', 'y', 'z'],
    10,
    11
);

One could use a temporary variable to either hold the original value ($row) if it is already an array reference or create an array reference to hold the original value (if it's a scalar). From then on you can use the cooked value in place of the original. For example:

foreach my $row (@array) {
    my $cooked = ref $row eq 'ARRAY' ? $row : [ $row ];
    print "@$cooked\n";
}

This outputs:

1 2 3 4 5
x y z
10
11

One can also eliminate the temporary variable:

foreach my $row (@array) {
    foreach my $item ( @{ ref $row eq 'ARRAY' ? $row : [ $row ] } ) {
        print "$item\n"
    }
    print "\n";
}

This directly evaluates the coercion code as an array (with @{ ... }) and iterates over it. The output is:

1
2
3
4
5

x
y
z

10

11

Similar code can be used, e.g., for hash references (ref $variable eq 'HASH')

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.