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++];
    }
}
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
wes
  • 734
  • 6
  • 14
  • 1
    What should the result be, if a) array1 has more OR b) less items than array2? – Glavić Sep 20 '12 at 20:26
  • 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. – wes Sep 20 '12 at 20:58
  • @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. – Lathan Sep 21 '12 at 00:57
  • VERY Closely Related: [Transpose and flatten two-dimensional indexed array where rows may not be of equal length](https://stackoverflow.com/q/25669227/2943403) and [PHP::How merge 2 arrays when array 1 values will be in even places and array 2 will be in odd places?](https://stackoverflow.com/q/5219138/2943403) and [Interleaving multiple arrays into a single array](https://stackoverflow.com/q/1860490/2943403) – mickmackusa Sep 01 '22 at 06:54

5 Answers5

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.

Glavić
  • 42,781
  • 13
  • 77
  • 107
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.

Glavić
  • 42,781
  • 13
  • 77
  • 107
Lathan
  • 853
  • 4
  • 14
  • 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 build – wes Sep 20 '12 at 20:49
  • 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...) – Lathan Sep 20 '12 at 21:10
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.

Micah
  • 313
  • 1
  • 4
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); 
Eineki
  • 14,773
  • 6
  • 50
  • 59
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
*/
mickmackusa
  • 43,625
  • 12
  • 83
  • 136