Insert elements from one array (one-at-a-time) after every second element of another array (un-even zippering)
Asked Answered
S

5

6

What would be an elegant way to merge two arrays, such that the resulting array has two items from the first array followed by a single item from the second array, repeating in this fashion?

$array1 = ['A1', 'A2', 'A3', 'A4', 'A5']; // potentially longer
$array2 = ['B1', 'B2', 'B3', 'B4', 'B5']; // potentially longer

Desired result:

['A1', 'A2', 'B1', 'A3', 'A4', 'B2', 'A5', 'B3', 'B4', 'B5']

I'm trying to do it using a for loop with multiple counters, but I don't know that the array lengths will be. I'm curious: is there a better way?

Here's a simplified version of what I'm currently doing:

$x = 0, $y = 0;
for ($i = 0; $i < $total_num_blocks; $i++) {
    if ($i % 3) {   // if there's a remainder, it's not an 'every 3rd' item
        $result[$i] = $projects[$x++];
    } else {
        $result[$i] = $posts[$y++];
    }
}
Serles answered 20/9, 2012 at 20:17 Comment(4)
What should the result be, if a) array1 has more OR b) less items than array2?Messieurs
yes, good question. no, i don't care about array keys; also if one array is longer than the other, $result can either just stop when one array is empty, OR contain the rest of the longer array. Strangely, both options would be fine for my particular use-case.Serles
@Glavic, nice 1 (or 2) liner. I just wish there was a way to do that route without the indexing. I really like how easy it it to understand the while( sizeof($array) ) syntax.Trisyllable
VERY Closely Related: Transpose and flatten two-dimensional indexed array where rows may not be of equal length and PHP::How merge 2 arrays when array 1 values will be in even places and array 2 will be in odd places? and Interleaving multiple arrays into a single arrayUraemia
E
5

This example will work regardless of the $a and $b array size.

<?php 

$a = ['A1', 'A2', 'A3', 'A4', 'A5'];
$b = ['BB1', 'BB2', 'BB3', 'BB4', 'BB5'];

for ($i = 0; $i < count($b); $i++) {
    array_splice($a, ($i+1)*2+$i, 0, $b[$i]);
}

echo "<pre>" . print_r($a, true) . "</pre>";

Output of this example is :

Array
(
    [0] => A1
    [1] => A2
    [2] => BB1
    [3] => A3
    [4] => A4
    [5] => BB2
    [6] => A5
    [7] => BB3
    [8] => BB4
    [9] => BB5
)

Warning: keys are NOT preserved!

This is PHP 5.4.x code, if you don't have it, replace [] with array() in $a and $b variables.

Emancipated answered 20/9, 2012 at 20:45 Comment(0)
T
1
while( sizeof($posts) >= 2 && sizeof($projects) >= 1){
    array_push($result,
            array_shift($posts),
            array_shift($posts),
            array_shift($projects)
        );
}
# you will need to handle the case if $posts doesn't have an even number of elements    

Note: This is destructive of $posts and $projects.

Trisyllable answered 20/9, 2012 at 20:28 Comment(2)
ah, that is more elegant, thanks. I also like that the code is structured such that it is more representative of the array i'm trying to buildSerles
The only downside is is $posts can be an odd number, or if you care about keeping the contents of $posts and $projects (but you could copy the arrays before...)Trisyllable
W
0
   $array1 = array('A1','A2','A3','A4','A5','A6');
   $array2 = array('B1','B2','B3','B4','B5','B6');

   $resultArray = array();

   //Reverse Second Array
   $array2R = array_reverse($array2);

   for ($i = 0; $i < count($array1); $i++){
       $resultArray[] = $array1[$i];
       if (($i) % 2)
           $resultArray[] = array_pop($array2R);
   }

   var_dump($resultArray);

Results with

array(9) { [0]=> string(2) "A1" [1]=> string(2) "A2" [2]=> string(2) "B1" [3]=> string(2) "A3" [4]=> string(2) "A4" [5]=> string(2) "B2" [6]=> string(2) "A5" [7]=> string(2) "A6" [8]=> string(2) "B3"}

Instead of keeping track of the second array, you can reverse the second array, and array_pop it. http://php.net/manual/en/function.array-pop.php

array_pop() pops and returns the last value of the array, shortening the array by one element. If array is empty (or is not an array), NULL will be returned. Will additionally produce a Warning when called on a non-array.

You may want to finish off the Resultant array by adding all remaining items from array2 to the end of your resultant array.

Wiggs answered 20/9, 2012 at 20:40 Comment(0)
S
0

I don't know if it is elegant or efficient, you have to run some benchmark by yourself, but there is a fun way to merge those arrays without loops:

$a = array("a1","a2","a3","a4","a5","a6","a7","a8"); 
$b = array("b1","b2","b3","b4","b5","b6","b7","b8"); 

function asymmetricInterleave($a, $aCount, $b, $bCount) { 
    $ax = array_chunk($a, $aCount); 
    $bx = array_chunk($b, $bCount); 

    $diff = count($ax) - count($bx); 

    $remainder = array(); 
    if ($diff > 0) { 
        list($ax, $remainder) = array_chunk($ax, count($bx)); 
    } else if ($diff < 0) { 
        list($bx, $remainder) = array_chunk($bx, count($ax)); 
    } 

    $result = array_merge(array_map('array_merge', $ax, $bx), $remainder); 
    return call_user_func_array('array_merge', $result); 
} 


$result = asymmetricInterleave($a, 2, $b, 1); 
var_export($result); 
Surrogate answered 21/9, 2012 at 11:8 Comment(0)
U
0

I've extended @Glavić's excellent, excellent looped array injection script to provide greater control of the frequency of insertion and the amount of data per insertion.

This script is designed to work with non-negative parameters. The input arrays may be of any length with no requirement to share the same length. The first array is modified to become the result array, so if you need to preserve its initial data, you'll need to save a copy before iterating.

Have a play by modifying the arrays, $afterEvery, and $insertCount in the 3v4l.org demo link.

Code: (Demo)

$a = ['A1', 'A2', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', 'A9'];
$b = ['B1', 'B2', 'B3', 'B4', 'B5', 'B6', 'B7', 'B8', 'B9'];

$afterEvery = 2;
$insertCount = 1;

for ($i = 0, $count = count($b); $i < $count; ++$i) {
    array_splice(
        $a,                                                  # the array to be modified
        $afterEvery + $afterEvery * $i + $insertCount * $i,  # each iteration must account for previously inserted elements 
        0,                                                   # do not consume any elements from $a
        array_slice($b, $i * $insertCount, $insertCount)     # push array of element(s) into $a
    );
}
var_export($a);

Cases with arithmetic breakdown:

/*
insert 2 after every 1                          every + (every * i) + (insert * i)
0 (insert @ 1)  ... A B B A A A A A A A A A A   1     + (1     * 0) + (2      * 0) = 1
1 (insert @ 4)  ... A B B A B B A A A A A A A   1     + (1     * 1) + (2      * 1) = 4
2 (insert @ 7)  ... A B B A B B A B B A A A A   1     + (1     * 2) + (2      * 2) = 7
3 (insert @ 10) ... A B B A B B A B B A B B A   1     + (1     * 3) + (2      * 3) = 10 
*/

/*
insert 1 after every 2                          every + (every * i) + (insert * i) 
0 (insert @ 2)  ... A A B A A A A A A A A A A   2     + (2     * 0) + (1      * 0) = 2 
1 (insert @ 5)  ... A A B A A B A A A A A A A   2     + (2     * 1) + (1      * 1) = 5
2 (insert @ 8)  ... A A B A A B A A B A A A A   2     + (2     * 2) + (1      * 2) = 8
3 (insert @ 11) ... A A B A A B A A B A A B A   2     + (2     * 3) + (1      * 3) = 11
*/

/*
insert 1 after every 3                          every + (every * i) + (insert * i) 
0 (insert @ 3)  ... A A A B A A A A A A A A A   3     + (3     * 0) + (1      * 0) = 3
1 (insert @ 7)  ... A A A B A A A B A A A A A   3     + (3     * 1) + (1      * 1) = 7
2 (insert @ 11) ... A A A B A A A B A A A B A   3     + (3     * 2) + (1      * 2) = 11
3 (insert @ 15) ... A A A B A A A B A A A B A   3     + (3     * 3) + (1      * 3) = 15
*/

/*
insert 3 after every 2                          every + (every * i) + (insert * i)
0 (insert @ 2)  ... A A B B B A A A A A A A A   2     + (2     * 0) + (3      * 0) = 2  
1 (insert @ 7)  ... A A B B B A A B B B A A A   2     + (2     * 1) + (3      * 1) = 7
2 (insert @ 12) ... A A B B B A A B B B A A B   2     + (2     * 2) + (3      * 2) = 12
3 (insert @ 17) ... A A B B B A A B B B A A B   2     + (2     * 3) + (3      * 3) = 17
*/
Uraemia answered 1/9, 2022 at 11:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.