2

So I should preface this by saying I've actually solved this, but the syntax is horrible so I want to see if theres a Perl-ish way of doing it which is nicer.

I have two arrays of length n (@genes and @names). I want to combine them into a single 2D array of paired values.

My approach right now is;

$Num = Number of elements in each array

my @genes = ();
foreach my $i ( 0 .. $num-1 ) {
    foreach my $j ( 0 .. 1 ) {
    if ($j == 0){ push @{ $genes[$i] }, $names[$i];}
    if ($j == 1){ push @{ $genes[$i] }, $lengths[$i];}
    }
}

But this requires an explicit line for each additional column (right now I have two - names and length). Also it's hideous. Code only a mother could love.

Any thoughts. Note that

@genes = (\@lengths, \@names);

Does not achieve what I want.

1
  • You did mean to say that you have two arrays @names and @length that you want to combine into a 2D-array @genes, I take it? Commented Jun 21, 2013 at 0:37

3 Answers 3

5

You can at least do this:

foreach my $i ( 0 .. $num-1 ) {
    push @genes, [$names[$i], $lengths[$i]];
}

If you don't care about the input arrays, you can consume them:

push @genes, [shift @names, shift @lengths] while @names;

There are also some modules you can use for iterating over multiple lists. For example, using List::MoreUtils::each_array:

use List::MoreUtils qw( each_array );
my $it = each_array( @names, @lengths );
while (my ($n, $l) = $it->()) {
    push @genes, [$n, $l];
}

Further, with List::MoreUtils::pairwise:

use List::MoreUtils qw( pairwise );
@genes = pairwise{ [our $a, our $b] } @names, @lengths;

Suggested by ysth, with Algorithm::Loops::MapCarE:

use Algorithm::Loops 'MapCarE';
@genes = MapCarE { \@_ } \( @names, @lengths );
Sign up to request clarification or add additional context in comments.

1 Comment

Awesome - that first syntax is clearly a much cleaner implementation (and in hind-site not in anyway Perl specific)
4

You could write

my @genes = map [ $names[$_], $lengths[$_] ], 0 .. $#names;

Comments

1

Incidentally, your original code can be cleaned up just by removing some unnecessary logic:

foreach my $i ( 0 .. $num-1 ) {
    foreach my $j ( 0 .. 1 ) {
        if ($j == 0){ push @{ $genes[$i] }, $names[$i];}
        if ($j == 1){ push @{ $genes[$i] }, $lengths[$i];}
    }
}

You're looping over 0 and 1, then testing if you're on 0 or 1, then performing one action or the other? Just perform the two actions each time:

foreach my $i ( 0 .. $num-1 ) {
  push @{$genes[$i]}, $names[$i];
  push @{$genes[$i]}, $lengths[$i]
}

Possibly you started out wanting to loop over the indexes, then thought you couldn't do this since the internal arrays didn't exist yet, and fell back to this push construction. But, er, the arrays don't exist until you push onto them here, either. Looping over the indexes was fine:

my @helper = (\@names, \@lengths);
foreach my $i ( 0 .. $#names ) {
    foreach my $j ( 0 .. 1 ) {
        $genes[$i][$j] = $helper[$j][$i];
    }
}

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.