'Explain how this array transposing and flattening function works

Here this function in PHP that allows to merge any N amount of different length arrays in a fashion that output array will be in next order: Array1[0],Array2[0],..,ArrayN[0],Array1[1],Array2[1],..,ArrayN[1]... :

    function array_zip_merge() {
      $output = array();
      // The loop incrementer takes each array out of the loop as it gets emptied by array_shift().
      for ($args = func_get_args(); count($args); $args = array_filter($args)) {
        // &$arg allows array_shift() to change the original.
        foreach ($args as &$arg) {
          $output[] = array_shift($arg);
        }
      }
      return $output;
    }

// test

$a = range(1, 10);
$b = range('a', 'f');
$c = range('A', 'B');
echo implode('', array_zip_merge($a, $b, $c)); // prints 1aA2bB3c4d5e6f78910

While I understand what each of built in functions in this example do on their own, I just cant wrap my mind how it all works together in this function, despite included explaining commentaries...

Can someone break it down for me, please? The function works great as is, its just driving me crazy that I don't understand how it works...

P.S: this function is taken from Interleaving multiple arrays into a single array question.



Solution 1:[1]

The arrays $a, $b and $c have 10, 6 and 2 elements respectively.

$a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
$b = ['a', 'b', 'c', 'd', 'e', 'f'];
$c = ['A', 'B'];

When you feed the arrays as arguments for the array_zip_merge() function, look at the for loop. The func_get_args() will set the $args with all the arguments supplied. On start of first for loop run,

$args = [$a, $b, $c];
count($args) = 3;

At the foreach loop the array_shift will return the first element of each array resulting the $output to be like

$output = [1, 'a', 'A'];

And the arrays now look like,

$a = [2, 3, 4, 5, 6, 7, 8, 9, 10];
$b = ['b', 'c', 'd', 'e', 'f'];
$c = ['B'];

At the end of the first for loop the array_filter function will test if any array is empty and remove it from $args. Same thing will happen at the second run, and by the end of the second for loop, the variables would look like

$a = [3, 4, 5, 6, 7, 8, 9, 10];
$b = ['c', 'd', 'e', 'f'];
$c = [];
$output = $output = [1, 'a', 'A', 2, 'b', 'B'];
//because $c is empty array_filter() removes it from $args
$args = [$a, $b];

So, on the third iteration of the for loop count($args) will return 2. When the last element of $b has been removed by array_shift the count($args) will return 1. The iteration will continue until all the arrays are empty

Solution 2:[2]

Inside array_zip_merge, the for statement always takes the first values of each array and add them to output variable respectively.

Because array_shift removes the element it returns, on every loop the first elements are different. When it gets empty because of it, the loop has nothing to do and breaks.

If you still dont understand, ask the specific part of the code you have trouble with please.

Solution 3:[3]

The custom function is effectively a transposing and flattening technique that will accommodate a non-matrix set of data. By non-matrix, I mean by pushing the three arrays into a single array, there will be columnar gaps in the data set. Using implode(array_merge(...array_map(null, $a, $b, $c))) will work by happenstance (because you are imploding the data with an empty string as glue, but in other cases, the generation of null elements will potentially skew the results.

I find the borrowed script to be very hard to interpret. The for loop would be much simpler as a foreach(). Instead of calling func_get_args(), the incoming columnar data could have been declared a variable using the spread operator in the function definition.

Here is one way to perform the 3-step task (without implode):

  1. Transpose the data from the 3 arrays (isolate columns of data)
  2. Filter null values out of the columnar data
  3. Flatten the filtered two dimensional array (array_merge(...$tranposedAndfiltered))

Code: (Demo)

var_export(
    array_merge(
        ...array_map(
            fn(...$col) => array_filter(
                $col,
                fn($v) => !is_null($v)
            ),
            $a,
            $b,
            $c
        )
    )
);

If it is simpler to perform the transposition without the spread operator and null filtering, just use nested foreach loops before flattening and imploding. (Demo)

$output = [];
foreach ([$a, $b, $c] as $i => $array) {
    foreach ($array as $k => $v) {
        $output[$k][$i] = $v;
    }
}
var_export(implode(array_merge(...$output)));

See Stack Overflow's array transpose canonical: Transposing multidimensional arrays in PHP

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 frz3993
Solution 2 abeyaz
Solution 3 mickmackusa