Home > Software engineering >  Perl push item to subarray
Perl push item to subarray

Time:09-18

I have 2 empty arrays that I add items to using a for loop.

@main = (
    "a 1 b 2 c 3 ",
    "d 4",
    "e 5 f 6 g 7 h 8",
    "i 9 j 10",
);
@arr1 = (); #only gets the letters
#supposed to look like:
#( 
#[a,b,c]
#[d]
#[e,f,g,h]
#[i,j]
#)

@arr2 = (); #only gets the numbers
#supposed to look like:
#( 
#[1,2,3]
#[4]
#[5,6,7,8]
#[9,10]
#)
for($i=0;@main;$i =1){
    @line = split(/\s /,shift(@main));
    push(@arr1,[]);
    push(@arr2,[]);
    while(@line){
        push(@arr1[$i],shift(@line));
        push(@arr2[$i],shift(@line));
    }
}

error:

Experimental push on scalar is now forbidden at index.pl line 29, near "))"
Experimental push on scalar is now forbidden at index.pl line 30, near "))"

It seems that @arr[$i] returns a reference to an array. How do I get this array and add items to it?

CodePudding user response:

The first argument of push must be an array, not an array slice. I think you wanted @{ $arr1[$i] } (but that's not your only problem). Make sure that you're using use strict; use warnings;! –ikegami

for(my $i=0;@main;$i =1){
    my @line = split(/\s /,shift(@main));
    push(@arr1,[]);
    push(@arr2,[]);
    while(@line){
#             ↓↓           ↓ 
        push( @{ $arr1[$i] } ,shift(@line));
        push( @{ $arr2[$i] } ,shift(@line));
    }
}

Not exactly sure how it works, probably turns the reference into an actual array of something along those lines.

Another solution I came up with myself, which adds the finished array after instead of adding values slowly:

for($i=0;@main;$i =1){
    @line = split(/\s /,shift(@main));
    @subArr1 = ();
    @subArr2 = ();
    while(@line){
        push(@subArr1,shift(@line));
        push(@subArr2,shift(@line));
    }
    push(@arr1,[@subArr1]);
    push(@arr2,[@subArr2]);
}

CodePudding user response:

As of perl 5.36, you can iterate over multiple elements of a list at once in a foreach loop, which lets you clean this up a bit:

#!/usr/bin/env perl
use 5.036;
# The next two lines aren't strictly needed because of the above, but
# I like to be explicit
use strict;
use warnings;
use experimental qw/for_list/;
use Data::Dumper;

my @main = (
    "a 1 b 2 c 3 ",
    "d 4",
    "e 5 f 6 g 7 h 8",
    "i 9 j 10",
    );
my (@arr1, @arr2);

for my $line (@main) {
    my (@sub1, @sub2);
    for my ($name, $num) (split " ", $line) {
        push @sub1, $name;
        push @sub2, $num;
    }
    push @arr1, \@sub1;
    push @arr2, \@sub2;
}

print Dumper(\@arr1, \@arr2);

You can use the same approach in older versions using natatime from the List::MoreUtils module (Available from CPAN or your OS's package manager):

#!/usr/bin/env perl
use strict;
use warnings;
use List::MoreUtils qw/natatime/;
use Data::Dumper;

my @main = (
    "a 1 b 2 c 3 ",
    "d 4",
    "e 5 f 6 g 7 h 8",
    "i 9 j 10",
    );
my (@arr1, @arr2);

for my $line (@main) {
    my (@sub1, @sub2);
    my $it = natatime 2, split(" ", $line);
    while (my ($name, $num) = $it->()) {
        push @sub1, $name;
        push @sub2, $num;
    }
    push @arr1, \@sub1;
    push @arr2, \@sub2;
}

print Dumper(\@arr1, \@arr2);

The thing these have in common is building up each subarray first, and then pushing references to them onto @arr1 and @arr2 at the end, removing the need for a lot of the arrayref dereferencing syntax. They also iterate directly over the elements you're storing in the subarrays instead of modifying the source directly with shift.

CodePudding user response:

Please inspect the following demonstration code for a compliance with your problem.

The code is based upon comments in OP's code with description what result arrays should look alike.

use strict;
use warnings;
use feature 'say';

use Data::Dumper;

my(@main,@arr1,@arr2);

@main = (
    "a 1 b 2 c 3 ",
    "d 4",
    "e 5 f 6 g 7 h 8",
    "i 9 j 10",
);

for ( @main ) {
    push(@arr1,[ /(\d )/g   ]);
    push(@arr2,[ /([a-z])/g ]);
}

say Dumper(\@arr1,\@arr2);

Output

$VAR1 = [
          [
            '1',
            '2',
            '3'
          ],
          [
            '4'
          ],
          [
            '5',
            '6',
            '7',
            '8'
          ],
          [
            '9',
            '10'
          ]
        ];

$VAR1 = [
          [
            'a',
            'b',
            'c'
          ],
          [
            'd'
          ],
          [
            'e',
            'f',
            'g',
            'h'
          ],
          [
            'i',
            'j'
          ]
        ];
  • Related