Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

interleave two arrays

by eg (Friar)
on Feb 03, 2001 at 02:02 UTC ( [id://56114]=CUFP: print w/replies, xml ) Need Help??

Given two array refs, this will interleave their elements, returing one long array. Handy for taking two arrays (@keys, @values) and combining them into a hash.

"Extra" elements will be placed at the end.

sub interleave { return @{$_[0]} unless @{$_[1]}; return @{$_[1]} unless @{$_[0]}; return shift(@{$_[0]}), shift(@{$_[1]}), &interleave; } print interleave( ['a'..'e'], [1..5] ), "\n"; my %hash = interleave( ['a'..'e'], [1..5] );

Replies are listed 'Best First'.
Re (tilly) 1: interleave two arrays
by tilly (Archbishop) on Feb 03, 2001 at 04:57 UTC
    Try your code with a pair of very large lists. You will quickly find that it is quadratic in both memory and space requirements.

    If you think about how much data is passed between subroutines you should be able to see why...

      Yes, I realize that. This came out of my toying with functional programming in perl. Most of the other solutions I've seen here have side effects that I was trying (for intellectual, rather than practical reasons) to avoid.

        Well a random tip then. Perl does particularly poorly when faced with deep recursion. However it certainly supports functional techniques, and I refer to several examples on my home node.

        BTW I already gave a solution to a generalization of the original problem which is O(n) and has no side-effects at Re (tilly) 1: interleaving lists.

Re: interleave two arrays
by runrig (Abbot) on Feb 03, 2001 at 02:30 UTC
Re: interleave two arrays
by Fastolfe (Vicar) on Feb 03, 2001 at 02:34 UTC
    This is also something that's come up in SOPW a few times. The most recent, interleaving lists, has some alternative implementations for those that are curious.
Re: interleave two arrays
by gryng (Hermit) on Feb 03, 2001 at 02:43 UTC
    Just an alternate way:

    sub interleave { my $y; while (@{ $_[0] } or @{ $_[1] }) { push @$y, shift @{ $_[0] } if @{ $_[0] }; push @$y, shift @{ $_[1] } if @{ $_[1] }; } return $y; }
    Ack, what have I done, this will surely lead to endless perl golf!

    Ciao,
    Gryn

    Update: Eek, this doesn't work. Please ignore -- I'm at work and will be a while to fix.

    Update: Ok that seems better. Thank you for your patience :)

Re: interleave two arrays
by scott (Chaplain) on Feb 04, 2001 at 04:15 UTC

    Shockingly (for me to see this question here), I just solved this problem two days ago.

    Try this:

    @foo = ( 1 , 3 , 5 , 7 ); @bar = ( 2 , 4 , 6 , 8 ); @baz = map { ( $_ , shift @bar ) } @foo;

    It suffers from losing the end of @bar if length( @bar ) > length ( @foo ) though.

      Scott, I tried your code on a similar problem that I had
      print STDOUT my @foobar = map { ( $_, shift @foo ) } @bar;
      and it worked, but when I turn on use warnings; I get the following error:
      Use of uninitialized value in print at line 3
      I've done some research and it seems that some people just complain about using warning and turn them off (I don't want to do that), and other say to re-work the code into a while loop. Anyone have any advice as to how I can keep the code while getting rid of the error?

        Hmmm ... this works for me with no comments from perl:

        use strict; use warnings; my @foo = ( 1 , 3 , 5 , 7 ); my @bar = ( 2 , 4 , 6 , 8 ); print STDOUT my @baz = map { ( $_ , shift @bar ) } @foo;
        I originally had a problem with `uninitialized' warnings when trying to reproduce your problem but it turned out to be because I had
        [ ... stuff ... ] my @baz = map { ( $_ , shift @bar ) } @foo; print STDOUT my @baz = map { ( $_ , shift @bar ) } @foo;
        This, of course, eats up @bar the first time so there's nothing left to shift the second time. :(

        What's on your lines 1 and 2?

        As for the general problem of warnings, I only turn warnings off for short one-shot scripts that I know will work even though I wrote bad code. Or, rather, I ignore them unless they get in the way of the output.

        I've never encountered a warning (from perl) that wasn't justified so if I'm doing anything at all important I eliminate them.

Re: interleave two arrays
by danger (Priest) on Feb 03, 2001 at 02:48 UTC

    This can also be used to interleave arrays, though it is more suited to iterating through them rather than just building the interleaved list (also, take note of chipmunk's correction to handle parallel undefs).

Re: interleave two arrays
by dkubb (Deacon) on Feb 03, 2001 at 09:44 UTC

    This uses a hash slice to achieve the same outcome:

    #!/usr/bin/perl -w use strict; my @keys = qw(a b c d); my @values = qw(1 2 3 4); print interleave(\@keys, \@values); sub interleave { my %interleaved; @interleaved{ @{$_[0]} } = @{$_[1]}; return %interleaved; }

      That only works if you consider "interleaving" to mean "throw away any pairs where the first array has duplicate values" and "sort the merged pairs in a pseudo-random order". (:

              - tye (but my friends call me "Tye")

        Thank you for pointing my mistake in using a hash slice to interleave two lists, tye. Here's my second attempt at interleaving two lists. This time I use an array slice, and (hope this makes sense) calculates the offset position for the two arrays in the final interleaved array:

        #!/usr/bin/perl -w use strict; use Data::Dumper qw(Dumper); my @keys = qw(a b c d e f g h i j); #is longer than my @values = qw(1 2 3 4 5); #this.. my @array = interleave(\@keys, \@values); print Dumper(\@array); #let's see what happens sub interleave { my $array1 = shift; my $array2 = shift; my(@even, @odd); push @even, $_ * 2 for 0..$#$array1; push @odd, $_ * 2 + 1 for 0..$#$array2; my @interleaved; @interleaved[@even, @odd] = (@$array1, @$array2); return wantarray ? @interleaved : \@interleaved; } __END__

        If one of the arrays is longer than the other, it will still interleave them as if they are the same length. This behaviour is essential if the interleaved array ever gets copied to a hash, or you need the array elements to be in pairs.

        The method that I used to calculate the offsets inside interleave() is the fastest I could find through benchmarking. If there is a faster/cleaner/better way, I would love to hear it.

Re: interleave two arrays
by mechagodzilla (Initiate) on Dec 21, 2010 at 08:18 UTC
Re: interleave two arrays
by kloro2006 (Initiate) on Feb 02, 2014 at 23:07 UTC
    #the following gives: a 1 b 2 c 3 @arr1 = qw(a b c); @arr2 = qw(1 2 3); while (@arr1) {push @arr3, shift @arr1,shift @arr2} print "@arr3"; #BUT why does the following give an infinite loop? @arr1 = qw(a b c); @arr2 = qw(1 2 3); while ($push @arr3, shift @arr1,shift @arr2){}
      See the documentation of push:
      Returns the number of elements in the array following the completed "push"

      Also, remove the dollar sign before "push".

      لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
        thanks. how do i edit an item i've already posted?

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://56114]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having an uproarious good time at the Monastery: (3)
As of 2024-04-25 22:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found