Merge two flat indexed arrays of equal size so that values are pushed into the result in an alternating fashion
Asked Answered
C

2

6

Suppose I have two arrays:

$a1 = array(0, 1, 2);
$a2 = array(3, 4, 5);

I want to be able to do a merge technique that alternates the array values and not just concatenate them. I want this result:

array(0, 3, 1, 4, 2, 5);

Is there a native way to do this as performance is an issue here since I need to do this thousands of times

Please note, I know I can do it like this:

for (var $i = 0; $i < count($a1); $i++) {
    newArray[] = $a1[$i];
    newArray[] = $b1[$i];
}

I'm looking for a built in way if there is a faster one.

Civism answered 6/8, 2012 at 11:33 Comment(5)
I'm not sure why a build-in function would be faster? This is basically what you want to do, isn't it?Elbow
I guess you could store elements of one array in odd positions and elements of other array at even position if you want to merge them in an alternate sequence.Kilt
@Civism I know you say performance is an issue, but I would be surprised if a built-in function would be noticably (if at all) faster than the loop approach, since any built in function would just have a C++ loop underneath it anyway.Northumbrian
@Northumbrian Well I know nothing about how PHP expands arrays, are they linked list in memory? Maybe a built in function would first alloc memory for the array once and then start inserting values. Not knowing any of the above... I asked this question.Civism
@Civism Firstly, you are slightly over-thinking it there, IMHO. The theoretical performance gains you are talking about are microseconds - with "thousands" of arrays that still only amounts to milliseconds. Secondly, it sounds like you are coming from the concept of an array in a low-level programming language, and PHP arrays are nothing like that in terms of the way they work underneath. You might find this an interesting read, albeit not directly related to the question.Northumbrian
T
11
$count = count($a1);
for ($i = 0; $i < $count; $i++) {
    $newArray[] = $a1[$i];
    $newArray[] = $b1[$i];
}

My work here is done.

$a1 = array(0,1,2);
$a2 = array(3,4,5);

$start = microtime(TRUE);

for($t = 0; $t < 100000; $t++)
{
    $newArray = array();
    $count = count($a1);
    for ($i = 0; $i < $count; $i++)
    {
        $newArray[] = $a1[$i];
        $newArray[] = $a2[$i];
    }
}
echo  round(microtime(TRUE) - $start, 2); # 0.6

$a1 = array(0,1,2);
$a2 = array(3,4,5);

$start = microtime(TRUE);

for($t = 0; $t < 100000; $t++)
{
    $newArray = array();
    for ($i = 0; $i < count($a1); $i++)
    {
        $newArray[] = $a1[$i];
        $newArray[] = $a2[$i];
    }
}
echo  round(microtime(TRUE) - $start, 2); # 0.85

So pre-counting array size will be ~1/4 [citation needed] (on freakin' 100.000 iterations you will gain 0.2 in total) faster. If you put count() inside loop, it will recount on every iteration. 1/4 seems to me a reasonably faster. If you are looking for compiled function, you can stop.

P.S. Benchmark is like bikini, it shows you everything, and nothing.

Tagmemics answered 6/8, 2012 at 11:44 Comment(4)
Hmm call me captain obvious but didn't he ask for a better way than that?Appanage
@Appanage better way than what?Alrick
Taking the count out of the loop technically is better.Embolic
I was hoping there would be something like implode($arr1, $arr2)Civism
A
1

Since you are "zippering" two indexed arrays, you can "transpose and flatten". I expect that this will not be quite as fast as using a for() or foreach(), but on small input arrays, there will be no noticeable drag on performance. In other words, when coding style or minimal declared variables is sought, then the following technique should be considered.

Code: (Demo)

$a1 = [0, 1, 2];
$a2 = [3, 4, 5];

var_export(
    array_merge(...array_map(null, $a1, $a2))
);

Output:

array (
  0 => 0,
  1 => 3,
  2 => 1,
  3 => 4,
  4 => 2,
  5 => 5,
)

As a funky little function-less approach, you can push the $a1 value from the foreach() value declaration and inside the loop's body, you can push the $a2 value. Feast your eyes... (Demo)

$result = [];
foreach ($a1 as $index => $result[]) {
    $result[] = $a2[$index];
}
var_export($result);
// same output as earlier snippet

For anyone that is hunting for an associative-safe technique, you could make looped slice or splice calls. Be aware that splice() will mutate/consume the input arrays in the process. (Slice Demo) (Splice Demo)

$result = [];
for ($i = 0, $count = count($a1); $i < $count; ++$i) {
    $result += array_slice($a1, $i, 1)
        + array_slice($a2, $i, 1);
}

or

$result = [];
while($a1 && $a2) {
    $result += array_splice($a1, 0, 1)
        + array_splice($a2, 0, 1);
}

p.s. I even build this utility snippet to offer more dynamic tooling of how many elements should be inserted and how frequently from each array while merging. See Insert elements from one array (one-at-a-time) after every second element of another array (un-even zippering).

Caveat: The above solutions were built for the asked question which has two arrays of equal length. If dealing with unequal arrays, you will need to determine how you wish the excess elements to be positioned in the result. Typically, it will be desirable to append the excess elements to the end of the result array. To accomplish this, determine length of each array, then use array_splice() to separate the excess elements for later. Use one of the techniques above, then append/push/merge the extra elements to the result array.

Aflame answered 9/9, 2021 at 8:36 Comment(2)
array_merge + array_map work fine with arrays that don't have the same size, but produce null values. Use array_filter to remove them.Whimsicality
I do not recommend array_filter() because it may have the side effect of removing valid/wanted values elsewhere in the array.Aflame

© 2022 - 2024 — McMap. All rights reserved.