coldy has asked for the wisdom of the Perl Monks concerning the following question:

I have two arrays
@a = (0,1,5,7,9,5,3) @b = (a,b,c,y,e,f,z,a,a)
Id like to place the values in @a everywhere accept where there is a 'y' or 'z' in @b in which case I want an 'I'
@new = (0,1,5,I,7,9,I,5,3)
I cant think of a simple way to do it, any ideas appreciated. Thanks in advance.

Replies are listed 'Best First'.
Re: add elements to array at certain positions
by repellent (Priest) on Oct 30, 2008 at 04:46 UTC
    my $i = 0; my @new = map { /^[yz]$/ ? 'I' : $a[$i++] } @b;
      Thanks. Is there a similar method if the arrays are strings?

        split the string(s) into a list of characters, use the map as shown, then join the result back together.


        Perl reduces RSI - it saves typing

        The method demonstrated below is devious and should be avoided in favour of something more conventional.

        my $stra = '0157953'; my $strb = 'abcyefzaa'; (my $new = $strb) =~ s/([yz])|./ (defined($1) ? 'I' : ($stra =~ m{(.)}sgc ? $1 : die ) ) /seg; print("$new\n"); # 015I79I53

        Update: Saner: (no nesting)

        my $stra = '0157953'; my $strb = 'abcyefzaa'; my $new = ''; while ($strb =~ /([yz])|./sg) { $new .= (defined($1) ? 'I' : ($stra =~ /(.)/sgc ? $1 : die ) ); } print("$new\n"); # 015I79I53

      $i should increment each time or else the value in @a that we didn't want will still be printed after the 'I'

      my $i = 0; my @new = map { ++$i; /^[yz]$/ ? 'I' : $a[$i] } @b;

      A solution I thought of with List::MoreUtils:

      use List::MoreUtils qw(pairwise); my @mine = pairwise { $b =~ /^[yz]$/ ? 'I' : $a; } @a, @b;

      Mine is not as nice because it doesn't work properly if both arrays are not the same size. You would have to add a check for undefined values or pad either array to match the other array's size. Perl also warns that $a and $b are only used once.

        The OP's example made it clear that no values from @a were to be skipped. $i should not increment each time, and even if it should, it should either be set to -1 before the 'loop', or incremented after the ternary. Your sample preincrements $i so $a[0] would always be skipped.


        Perl reduces RSI - it saves typing
Re: add elements to array at certain positions
by GrandFather (Saint) on Oct 30, 2008 at 04:44 UTC

    If you don't mind destroying the two input arrays then an easy way is:

    use warnings; use strict; my @a = qw(0 1 5 7 9 5 3); my @b = qw(a b c y e f z a a); my @new; while (@a && @b) { my $nextB = shift @b; if ($nextB =~ /[yz]/) { push @new, 'I'; } else { push @new, shift @a; } } print "@new";

    Prints:

    0 1 5 I 7 9 I 5 3

    If you don't like destroying the input arrays, make a copy first. If they are very large and copying them is not an option come back for a slightly uglier solution.

    Update: actually it's not really uglier:

    use warnings; use strict; my @a = qw(0 1 5 7 9 5 3); my @b = qw(a b c y e f z a a); my @new; my $scanA = 0; for my $nextB (@b) { if ($nextB =~ /[yz]/) { push @new, 'I'; } else { last if $scanA >= @a; push @new, $a[$scanA++]; } } print "@new";

    Output is the same as above (of course).


    Perl reduces RSI - it saves typing
Re: add elements to array at certain positions
by johngg (Canon) on Oct 30, 2008 at 15:42 UTC
    You could use splice and reverse to modify @a in place.

    use strict; use warnings; my @a = qw{ 0 1 5 7 9 5 3 }; my @b = qw{ a b c y e f z a a }; die qq{Element count mismatch\n} unless scalar @b == scalar @a + grep m{[yz]}, @b; print qq{@a\n@b\n}; my $offA = $#a; foreach my $offB ( reverse 0 .. $#b ) { if( $b[ $offB ] =~ m{[yz]} ) { splice @a, $offA + 1, 0, q{I}; } else { $offA --; } } print qq{@a\n};

    The output.

    0 1 5 7 9 5 3 a b c y e f z a a 0 1 5 I 7 9 I 5 3

    I hope this is of interest.

    Cheers,

    JohnGG