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

689

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 afor 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++];
    }
}
590

Answer

Solution:

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.

616

Answer

Solution:

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.

702

Answer

Solution:

   $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.

414

Answer

Solution:

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); 
553

Answer

Solution:

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
*/

People are also looking for solutions to the problem: php - Absolute positioned span not resizing accordingly

Source

Didn't find the answer?

Our community is visited by hundreds of web development professionals every day. Ask your question and get a quick answer for free.

Ask a Question

Write quick answer

Do you know the answer to this question? Write a quick response to it. With your help, we will make our community stronger.

Similar questions

Find the answer in similar questions on our website.