Louis Loudog Trottier
Louis Loudog Trottier

Reputation: 1377

Push columnar values of multiple arrays (of different lengths) to form a flat array

I have from 3 to 10 arrays containing 20 to 50 value each. I want to merge them into a single, flat, indexed array, but not the way array_merge works.

I rather want to take the 1st value of the 1st array, then the 1st value of the second array, then the 1st value of the third array and so on.

Keys are not an issue here, It could be numerical or associative array, I'm just keeping the values, not the original keys.

Values in real application can be mixed so merging and sorting afterwards is not an option.


So if I had 3 arrays like so:

$array1 = array('1-1', '1-2', '1-3');
$array2 = array('2-1', '2-2', '2-3', '2-4');
$array3 = array('3-1', '3-2');

I would need the result to be an array containing the value in this order:

1-1, 2-1, 3-1, 1-2, 2-2, 3-2, 1-3, 2-3, 2-4

So far I have this piece of code :

$array = array($array1, $array2, $array3);
$newarray = array();
foreach ($array as $a) {
  $v = array_shift($a);
  if (isset($v)) {
    $newarray[] = $v;
  }
}
print_r($newarray);

but, of course, it only runs the 1st set of value and give me:

Array ( [0] => 1-1 [1] => 2-1 [2] => 3-1 )

array_shift is what I need here because it remove the value from the old array (I don't need it anymore) and I move it to the new array (if it isset). see: http://php.net/manual/en/function.array-shift.php

I'm pretty sure I will need a function and loop it until all arrays are empty, but I just can't wrap my head around this one. Any help will be greatly appreciated. Or even better if someone knows a native php function that does that.

Upvotes: 2

Views: 298

Answers (6)

mickmackusa
mickmackusa

Reputation: 47904

While it is sexy to use array_map() to transpose array data so that columns of data can be easily extracted, the problem is that when processing data structures which are not perfectly shaped matrices, you will have null values generated.

Other answers fix the null population by calling array_filter() to mop up all of the unwanted nulls, but this will do harm to your result if you have false, null or zero-ish values in your data set that you actually wish to keep.

Also, this question states that key may be numeric or associative. This is another reason why transposing with array_map() is less attractive -- because it will choke on arrays with non-numeric keys.

While less elegant and sexy than some earlier posted answers, this answer wins out in terms of reliability because it will never destroy falsey values.

Add all arrays into a parent array (container), then iterate those arrays one at a time. "Shift" the first element off of each array and push it into the result array. If an array has no more elements, then unset() it so that it no longer occurs as a child of the parent array. (The original array variable will not be destroyed; only its copied version which exists inside the container.)

When the parent array has no more children, then stop looping.

Code: (Demo)

$array1 = [2 => 0, 0 => 1, 1 => '2'];
$array2 = [false, true, 'false' => false, 'true' => true];
$array3 = [null, 'null'];

$all = [$array1, $array2, $array3];
$result = [];
while ($all) {
    foreach ($all as $index => &$array) {
        $result[] = array_shift($array);
        if (!$array) {
            unset($all[$index]);
        }
    }
}
var_export($result);

Output:

array (
  0 => 0,
  1 => false,
  2 => NULL,
  3 => 1,
  4 => true,
  5 => 'null',
  6 => '2',
  7 => false,
  8 => true,
)

Upvotes: 0

Brenton Alker
Brenton Alker

Reputation: 9072

Here's a simple one-liner...

$arr1 = ['1-1', '1-2', '1-3'];
$arr2 = ['2-1', '2-2', '2-3', '2-4'];
$arr3 = ['3-1', '3-2'];

$result = array_filter(call_user_func_array(array_merge, array_map(null, $arr1, $arr2, $arr3)));

So, maybe not so simple... Some explanation.

array_map(null, ...); makes use of the feature of array_map explained in example 4 of the PHP docs but without any transformation to the values (hence, null).

So, this will give you:

array(4) {
  [0]=>
  array(3) {
    [0]=>
    string(3) "1-1"
    [1]=>
    string(3) "2-1"
    [2]=>
    string(3) "3-1"
  }
  [1]=>
  array(3) {
    [0]=>
    string(3) "1-2"
    [1]=>
    string(3) "2-2"
    [2]=>
    string(3) "3-2"
  }
  [2]=>
  array(3) {
    [0]=>
    string(3) "1-3"
    [1]=>
    string(3) "2-3"
    [2]=>
    NULL
  }
  [3]=>
  array(3) {
    [0]=>
    NULL
    [1]=>
    string(3) "2-4"
    [2]=>
    NULL
  }
}

Then, we need to "flatten" the array, concatenating all the sub-arrays together. This is achieved using array_merge, but that doesn't take an array, so we use call_user_func_array to feed the array into it's arguments.

Then the array_filter to remove the nulls (introduced by array_map due to the original arguments not being the same length).

Upvotes: 3

scrowler
scrowler

Reputation: 24405

Just added for reference, if you're running PHP 5.6+ you can take advantage of the new ... operator to signify n number of arguments in a function declaration and/or call. Implementation would be something like this (reference):

function merge_vertical(...$arrays) {
    return array_filter(array_merge(...array_map(null, ...$arrays)));
}

print_r(merge_vertical($array1, $array2, $array3));

Output:

Array
(
    [0] => 1-1
    [1] => 2-1
    [2] => 3-1
    [3] => 1-2
    [4] => 2-2
    [5] => 3-2
    [6] => 1-3
    [7] => 2-3
    [10] => 2-4
)

Upvotes: 2

Brian Gerhards
Brian Gerhards

Reputation: 849

I would recommend hashing through all of the arrays in a for loop.

$array1=array('1-1','1-2','1-3');
$array2=array('2-1','2-2','2-3','2-4','2-5');
$array3=array('3-1','3-2');

$maxSize=max(count($array1),count($array2),count($array3));

$array=array($array1,$array2,$array3);

$newarray=array();

for ($i = 0; $i < $maxSize; $i++) {
  for ($x = 0; $x < count($array); $x++) {
    if(count($array[$x]) > $i){
      $newarray[] = $array[$x][$i];
    }
  }
}
print_r($newarray);

Upvotes: 1

Kanishka Panamaldeniya
Kanishka Panamaldeniya

Reputation: 17576

Try this code

$array1=array('1-1','1-2','1-3');
$array2=array('2-1','2-2','2-3','2-4');
$array3=array('3-1','3-2');

$array=array($array1,$array2,$array3);
$array2 = $array ;

function cmp($a, $b){
    return (count($b) - count($a));
}
usort($array2, 'cmp');

$xax_length = count($array2[0]);

$newarray=array();
for($i=0;$i<$xax_length;$i++) {

   foreach($array as &$a){
      $v=array_shift($a);
      if(isset($v)){
        $newarray[]=$v;
      }
   }
}
print_r($newarray);

Upvotes: 1

Wahib Mkadmi
Wahib Mkadmi

Reputation: 646

You can use the + operator.

$c = $a + $b

Good luck !

Upvotes: -2

Related Questions