4

What are the operations allowed on array, while iterating through it? Is it possible to shift/unshift, pop/push, delete elements without confusing the iterator?

Is that any different for adding/removing key-value pair from hash?

Thank you for your help.

mjp
  • 205
  • 2
  • 11
  • Sure. I'm trying to simplify multiple arrays comparison by not recreating yet another data structure. I think, however, what is useful for me may not necessarily sound useful for you and vice-verse. – mjp Oct 23 '14 at 08:25
  • 1
    You can use grep/map to filter/transform/add elements to list, and assign it back to array. – mpapec Oct 23 '14 at 08:27

2 Answers2

5

You can assign to existing elements, but should not add or remove them. So no shift, unshift, pop, push, or splice. perlsyn:

If any part of LIST is an array, foreach will get very confused if you add or remove elements within the loop body, for example with splice. So don't do that.

If you are iterating over a hash with each, you should also avoid adding or removing elements, except that you are explicitly allowed to remove the current element. each:

If you add or delete a hash's elements while iterating over it, the effect on the iterator is unspecified; for example, entries may be skipped or duplicated--so don't do that. Exception: It is always safe to delete the item most recently returned by each(), so the following code works properly:

But as it says, the worst that could happen is entries being skipped or duplicated; modifying an array you are looping over, on the other hand, can lead to segfaults.

ysth
  • 88,068
  • 5
  • 112
  • 203
  • I thought of changing element of an array at the beginning as being not the most logical but playing with the last element sounds somewhat more natural - it does even work. I get your point though. Thanks. – mjp Oct 23 '14 at 08:08
  • I can't find any information about operations on array with only scalars as LIST elements. Do you think that the same rules apply? – mjp Oct 23 '14 at 08:15
  • no, the same rules do not apply; not sure what you mean by "playing with". – ysth Oct 23 '14 at 13:49
  • Don't know if a segfault is possible, but dying is. `perl -E'@a=qw( a b c ); for ((),@a) { @a=(); say } say "ok"'` – ikegami Oct 23 '14 at 20:03
2

As ysth has already pointed out, it is unwise to attempt to modify an array while iterating directly on its elements.

However, if one does want to modify an array dependent on the element values, the trick is to do it in reverse index order.

For example, say I have an array of numbers. I would like modifier the array so that every multiple of 4 has a string inserted after it, and every multiple of 5 is removed. I would accomplish that using the following:

use strict;
use warnings;

my @array = ( 1 .. 20 );

for my $i ( reverse 0 .. $#array ) {
    # Insert after multiples of 4
    if ( ( $array[$i] % 4 ) == 0 ) {
        splice @array, $i + 1, 0, "insert";
    }

    # Remove multiples of 5
    if ( ( $array[$i] % 5 ) == 0 ) {
        splice @array, $i, 1;
    }
}

use Data::Dump;
dd @array;

Outputs:

(
  1 .. 4,
  "insert",
  6,
  7,
  8,
  "insert",
  9,
  11,
  12,
  "insert",
  13,
  14,
  16,
  "insert",
  17,
  18,
  19,
  "insert",
)

Alternatively, if you want to transform an array, it's also possible to use map like so:

my @newarray = map {
    (   ( ($_) x !!( $_ % 5 ) ),         # Remove multiples of 5
        ( ('insert') x !( $_ % 4 ) ),    # Insert After multiples of 4
        )
} ( 1 .. 20 );

use Data::Dump;
dd @newarray;
Miller
  • 34,344
  • 4
  • 33
  • 55