Antillar Maximus
Antillar Maximus

Reputation: 227

Selecting elements from an array and putting it in another array in Perl

I have an array containing 10 numbers. I want to pick numbers in array index 0,2,4,6,8 and put them in a new array. Likewise with index 1,3,5,7,9. I am new to Perl (started a few days ago).

My program:

my @b;
@a = (1,2,3,4,5,6,7,8,9,10);
for($i=0;$i<=$#a;$i++)
   {
    push(@b,$a[$i+1]);
   }
print "@b";

What am I doing wrong?

Upvotes: 0

Views: 2481

Answers (5)

G. Cito
G. Cito

Reputation: 6378

List::MoreUtils has an indexes function:

use List::MoreUtils qw{indexes} ;
use 5.10.0 ;                                              
my @a = (1,2,3,4,5,6,7,8,9,10) ; 

# index of array where even
say foreach indexes { $_ % 2 == 0 } @a ;

# index of array where odd
say foreach indexes { $_ % 2 != 0 } @a ;

I admit this may be sort of inelegant and it's possibly cheating here to use a module - especially one that is not in CORE. It would be convenient if List::MoreUtils and List::Utils were just one CORE module, but still not as elegant as some the other answers here.

Upvotes: 1

David W.
David W.

Reputation: 107030

A few things:

  • Use the pragmas use strict; and use warnings;. These will catch a lot of errors. If you use use strict;, you'll have to declare your variables with my (sometimes you'll use our, but 99% of the time, you'll use my)
  • In your for loop, you're using the default variable $_. This variable is evil for a variety of reasons. (One, it's global in scope, so something else could change this variable on your and you wouldn't know.). Declare your variables except in situations where you must use $_.
  • Standard is to put the { on the line with the for and while. Another is to avoid the C style for loop (and to avoid foreach which is just an alias to for)
  • Use spaces. It's much easier to read $i <= $#a than $i<=$a.

Here's my interpretation of your program:

#! /usr/bin/env perl

use strict;
use warnings;
use feature qw(say);  #A nicer 'print'

my @a = qw(12 13 14 15 16 17 18 19 20);
my @even;
my @odd;

for my $element (0..$#a) {
if ( $element % 2 ) {
    push @odd, $a[$element];
}
else {
    push @even, $a[$element];
}
}
say '@even = ' . join ': ', @even;
say '@odd = ' . join ': ', @odd;

The output:

@even = 12: 14: 16: 18: 20
@odd = 13: 15: 17: 19
  • Note my for loop. I use the 0..$#a to go through each element of the array. The $# is returns the last index of the array. Note that this is easier to understand than the for($i=0;$i<=$#a;$i++) that you used. It's one of the reasons why C style for loops are discouraged.
  • I use the modulo operator % to parse my even/odd. Modulo is like remainder division. If the number is odd, the modulo % 2 will be a 1. Otherwise, it's zero. Modulo operations are great for anything that works on a cycle.

But let's get back to your program. Here's your original code with a few minor tweaks.

  • I added the use strict; and use warnings;. These catch about 99% of your programming errors.
  • I use use feature qw(say); because say is nicer when it comes to debugging. I can take a statement, copy it, and then throw say qq(...); around it and see what it's doing.
  • I added a bunch of say statements to reveal the logic of your code.

Let's watch what happens. Here's your program slightly modified:

#! /usr/bin/env perl

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

my @b;
my @a = (1,2,3,4,5,6,7,8,9,10);
my $i;
for($i=0; $i<=$#a; $i++) {
    say "Index = $i  Element = $a[$i + 1]";
    say qq(push(\@b, $a[$i+1]););
    push(@b,$a[$i+1]);
}
print "@b";

And here's the output:

Index = 0  Element = 2
push(@b, 2);
Index = 1  Element = 3
push(@b, 3);
Index = 2  Element = 4
push(@b, 4);
Index = 3  Element = 5
push(@b, 5);
Index = 4  Element = 6
push(@b, 6);
Index = 5  Element = 7
push(@b, 7);
Index = 6  Element = 8
push(@b, 8);
Index = 7  Element = 9
push(@b, 9);
Index = 8  Element = 10
push(@b, 10);
Use of uninitialized value in concatenation (.) or string at ./test.pl line 11.
Index = 9  Element = 
Use of uninitialized value within @a in concatenation (.) or string at ./test.pl line 12.
push(@b, );
Use of uninitialized value $b[9] in join or string at ./test.pl line 15.

I can see the how each push statement is being executed, and look at that, you're pushing in each and every element. Actually, you're not because you used $a[$i+1] as what you're pushing.

Using use warnings and I can see that I am trying to push the non-existant $a[10] into your @b array.

Let's change your for loop to go to every other element

#! /usr/bin/env perl

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

my @b;
my @a = qw(1 2 3 4 5 6 7 8 9 10);
my $i;
for ($i=0; $i <= $#a; $i += 2) {
    push @b, $a[$i];
}

The first element is $a[0]. The next element in the loop is $a[2] because I added 2 to the index instead of just incrementing it by 1. Now, I'll go through all the even elements and skip all of the odd elements.

And the output:

1 3 5 7 9

(Note that $a[0] = 1. That's why they're all odd numbers. It's why I started at 12 in my program, so $a[0] = 12 which is the even number).

My preference would be to use the while and avoid the for(...; ...; ...) construct:

#! /usr/bin/env perl

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

my @b;
my @a = qw(1 2 3 4 5 6 7 8 9 10);
my $i = 0;
while ( $i < $#a ) {
    push @b, $a[$i];
    $i += 2;
}

Upvotes: 1

hwnd
hwnd

Reputation: 70722

Simple.

my @a = (1,2,3,4,5,6,7,8,9,10);

my (@even, @odd);

for ( @a ) {
   $_ % 2 ? push @odd, $_ : push @even, $_;
}

Upvotes: 2

mpapec
mpapec

Reputation: 50637

I suggest avoiding for loop as it's easier to make mistake somewhere in its usage, and use foreach

my @a = (1,2,3,4,5,6,7,8,9,10);

my (@even, @odd);
foreach my $i (0 .. $#a) {

  if ($i % 2) { push @odd, $a[$i] } else { push @even, $a[$i] }
}

You can also use map to test array index modulo % 2, and then for @even decide to filter it by () or take value for it using $a[$_]

my @even = map { $_%2 ? () : $a[$_] } 0 .. $#a;
my @odd = map {  $_%2 ? $a[$_] : () } 0 .. $#a;

Upvotes: 2

Barmar
Barmar

Reputation: 780673

Even:

for($i=0;$i<=$#a;$i+=2)
   {
    push(@b,$a[$i]);
   }

Odd:

for($i=1;$i<=$#a;$i+=2)
   {
    push(@b,$a[$i]);
   }

Upvotes: 1

Related Questions