How can I merge multiple flat arrays of unknown depth, transpose them, then form a 1-dimensional array?

StackOverflow https://stackoverflow.com/questions/19478981

Question

I have 3 arrays like this:

$a = array(
 0 => 'a1',
 1 => 'a2',
 2 => 'a3'
);

$b = array(
 0 => 'b1',
 1 => 'b2',
 2 => 'b3'
);

$c = array(
 0 => 'c1',
 1 => 'c2',
 2 => 'c3'
);

and I like to have something like this:

$r = array(
 0 => 'a1',
 1 => 'b1',
 2 => 'c1',
 3 => 'a2',
 4 => 'b2',
 5 => 'c2',
 6 => 'a3',
 ....
 ...
);

How can I do this AND enjoy the ability to use more then 3 input arrays?

EDIT:

I have tried this:

$a = array(
        0 => 'a1',
        1 => 'a2',
        2 => 'a3',
        3 => 'a4'
    );
    $b = array(
        0 => 'b1',
        1 => 'b2',
        2 => 'b3'
    );
    $c = array(
        0 => 'c1',
        1 => 'c2',
        2 => 'c3',
        3 => 'c4',
        4 => 'c5',
        5 => 'c6'

    );

    $l['a'] = count($a);
    $l['b'] = count($b);
    $l['c'] = count($c);

    arsort($l);
    $largest = key($l);
    $result = array();
    foreach ($$largest as $key => $value) {
        $result[] = $a[$key];
        if(array_key_exists($key, $b)) $result[] = $b[$key];
        if(array_key_exists($key, $c)) $result[] = $c[$key];

    }
    print_r($result);

Output: Array ( [0] => a1 [1] => b1 [2] => c1 [3] => a2 [4] => b2 [5] => c2 [6] => a3 [7] => b3 [8] => c3 [9] => a4 [10] => c4 [11] => [12] => c5 [13] => [14] => c6 )

This works but the code isn't nice. Does anyone have a better solution?

Solution: I updated the post from @salathe with some dynamic feature

function comb(){
    $arrays = func_get_args();
    $result = array();
    foreach (call_user_func_array(array_map, $arrays) as $column) {
        $values = array_filter($column, function ($a) { return $a !== null; });
        $result = array_merge($result, $values);
    }
    return $result;
}
print_r(comb(null,$a,$b,$c,$d,....));
Était-ce utile?

La solution

You could make use the array_map() function to do most of the hard work.

In the example, the code inside the loop just takes the value from each array (null if there is not a corresponding value) and if there is a value, appends them to the $results array.

Example

$result = array();
foreach (array_map(null, $a, $b, $c) as $column) {                                          
    $values = array_filter($column, function ($a) { return $a !== null; });
    $result = array_merge($result, $values);
}
var_export($result);

Output

array (
  0 => 'a1',
  1 => 'b1',
  2 => 'c1',
  3 => 'a2',
  4 => 'b2',
  5 => 'c2',
  6 => 'a3',
  7 => 'b3',
  8 => 'c3',
  9 => 'a4',
  10 => 'c3',
  11 => 'c3',
  12 => 'c3',
)

Autres conseils

Need some coding:

function customMerge()
{
    $arrays = func_get_args();
    $res = array();
    $continue = true;
    while($continue){
       $continue = false;
       for($j=0;$j<count($arrays); $j++){
          if($pair = each($arrays[$j]){
              if(is_numeric($pair[0])
                  $res[] = $pair[1];
              else
                  $res[ $pair[0] ] = $pair[1];
              $continue = true;
          }
       }
    }
    return $res;
}  

$res = customMerge($arr1, $arr2, $arr3, ...);

sorry for my previous answer, misread the question. here's what you need:

$arrays = array($a,$b,$c);

$new = array();

$count = count($arrays);
while(count($arrays) > 0) {

    for($i = 0; $i < $count; $i++) {
        if (isset($arrays[$i])) {
        array_push($new, array_shift($arrays[$i]));
        if (count($arrays[$i]) == 0) {
            unset($arrays[$i]);
            }
        }
    }
}

even for the arrays with not equal sizes:

$a = array(
 0 => 'a1',
 1 => 'a2',
 2 => 'a3',
 3 => 'a4'
);

$b = array(
 0 => 'b1',
 1 => 'b2',
 2 => 'b3'
);

$c = array(
 0 => 'c1',
 1 => 'c2',
 2 => 'c3',
 3 => 'c4'
);

you'll get the result:

Array
(
    [0] => a1
    [1] => b1
    [2] => c1
    [3] => a2
    [4] => b2
    [5] => c2
    [6] => a3
    [7] => b3
    [8] => c3
    [9] => a4
    [10] => c4
)

You could do something like this:

function getMaxLength(array $arrays) {
    $len = count($arrays);

    if($len == 0) {
        return 0;
    }

    $max = count($arrays[0]);
    for($i = 1; $i < $len; $i++) {
        $count = count($arrays[$i]);
        if($count > $max) {
            $max = $count;
        }
    }

    return $max;
}

function combine() {
    $arrays = func_get_args();
    $maxLength = getMaxLength($arrays);
    $combined = array();
    for($i = 0; $i < $maxLength; $i++) {
        foreach($arrays as $array) {
            if(isset($array[$i])) {
                $combined[] = $array[$i];
            }
        }
    }
    return $combined;
}

print_r(combine($a, $b, $c));

This logic sucks.. Works though

<?php

$a = array(
    0 => 'a1',
    1 => 'a2',
    2 => 'a3'
);

$b = array(
    0 => 'b1',
    1 => 'b2',
    2 => 'b3'
);

$c = array(
    0 => 'c1',
    1 => 'c2',
    2 => 'c3',
    3 => 'c4',
    4 => 'c5' 
);

$val=5; //Size of the largest array  (largest array is c)

$d=array();
for($i=0;$i<$val;$i++)
{
    $d[]=$a[$i];
    $d[]=$b[$i];
    $d[]=$c[$i];
}

//Clearing empty values
foreach ($d as $key=>$value)
if (empty($value))
    unset($d[$key]);



 //Consecutive Keys 
       $finalArray = array_values($d);
       print_r($finalArray);

OUTPUT :

Array ( [0] => a1 [1] => b1 [2] => c1 [3] => a2 [4] => b2 [5] => c2 [6] => a3 [7] => b3 [8] => c3 [9] => c4 [10] => c5 )

Assumed count($a)=count($b)=count($c) , this can be done as:

<?php
   $var = array();
   for($i=0;$i<count($a);$i++)
   {
    array_push($var,$a[$i]);
    array_push($var,$b[$i]);
    array_push($var,$c[$i]);
    }
  print_r($var);
 ?>

This will result in:

Array ( [0] => a1 [1] => b1 [2] => c1 [3] => a2 [4] => b2 [5] => c2 [6] => a3 [7] => b3 [8] => c3 )

Edit: for @eggyal
I tried this out:

<?php
 $a = array(
    0 => 'a1',
    1 => 'a2',
    2 => 'a3',
    3 => 'a4'
);
$b = array(
    'x' => 'b1',
    'y' => 'b4',
    'z' => 'b3'
);
$c = array(
    0 => 'c1',
    'p' => 'c2',
    2 => 'c3',
    3 => 'c3',
    'q' => 'c3',
    5 => 'c3'

);
$d = array_merge($b,$a,$c);//place arrays in any order like $a,$b,$c or $b,$a,$c or $c,$b,$a
sort($d);
print_r($d);
?>

This resulted in:

Array ( [0] => a1 [1] => a2 [2] => a3 [3] => a4 [4] => b1 [5] => b3 [6] => b4 [7] => c1 [8] => c2 [9] => c3 [10] => c3 [11] => c3 [12] => c3 )

I am not sure this satisfies you or not. But, i think the merging still takes place. However, it does not preserve the keys.

Can be done with sorting

$arr = array_merge($a,$b,$c);

foreach ($arr as $key => $val) {
  $numsort[$key] = substr($val,1);
  $charsort[$key] = substr($val,0,1);
}

array_multisort($arr, SORT_ASC, SORT_NUMERIC, $numsort, $arr, SORT_ASC, $charsort);

// result

Array
(
    [0] => a1
    [1] => b1
    [2] => c1
    [3] => a2
    [4] => b2
    [5] => c2
    [6] => a3
    [7] => b3
    [8] => c3
)

Your case is just merge with some specific ordering. So clear way to do that is:

  1. merge arrays
  2. sort result

Code example:

$merged = array_merge($a, $b, $c);
usort($merged, function($left, $right) {
        if (substr($left, 1) == substr($right, 1)) {//if numbers equal check letters
                return (substr($left, 0, 1) < substr($right, 0, 1)) ? -1 : 1;
        }
        return (substr($left, 1) < substr($right, 1)) ? -1 : 1;
});

print_r($merged);

output:

Array
(
    [0] => a1
    [1] => b1
    [2] => c1
    [3] => a2
    [4] => b2
    [5] => c2
    [6] => a3
    [7] => b3
    [8] => c3
)

There is more generic solution (all first rows are subsequent, all second rows all subsequent ...):

$result = call_user_func_array('array_merge', array_map(
        function () {
                return array_filter(func_get_args(), function ($element) {
                                return $element !== null;
                });
        } , $a, $b, $c)
);

print_r($result);

I answered a duplicate before finding this page. My solutions below use ...$row for the input data, but for this question just switch ...$rows for $a, $b, $c. (I have since deleted my other answer and hammered the duplicate question using this page.)

My thoughts on functional one-liners (I've split it and tabbed it for readability) are as follows. Note that early filtering will mean minimizing "useless data" handling and late filtering will make fewer function calls.

Late filtering with greedy/lazy/falsey filtering: (Demo)

var_export(
    array_filter(          #remove nulls
        array_merge(       #flatten the unpacked elements
            ...array_map(  #unpack
                null,      #transpose
                ...$rows   #transpose
            )              #unpack
        )                  #flatten the unpacked elements    
    )                      #remove nulls
);

Condensed one-liner syntax: (Demo)

var_export(array_filter(array_merge(...array_map(null, ...$rows))));

Intermediate filtering with greedy/lazy/falsey filtering: (Demo)

var_export(
    array_merge(              #flatten the unpacked elements
        ...array_filter(      #remove nulls from transposing then unpack
            array_map(        #transpose
                null,         #transpose
                ...$rows      #transpose
            )                 #transpose
        )                     #remove nulls from transposing then unpack
    )                         #flatten the unpacked elements
);

Or do early filtering... (with greedy/lazy/falsey filtering)

Code: (Demo)

var_export(
    array_merge(                     #flatten the unpacked elements
        ...array_map(                #unpack
            function() {             #transpose discarding nulls
                return array_filter( #transpose discarding nulls
                    func_get_args()  #transpose discarding nulls
                );                   #transpose discarding nulls
            },                       #transpose discarding nulls
            ...$rows                 #transpose discarding nulls
        )                            #unpack
    )                                #flatten the unpacked elements
);

Or early filtering with greedy/lazy/falsey filtering and more "splatting": (Demo)

var_export(
    array_merge(                         #flatten the unpacked elements
        ...array_map(                    #unpack
            function(...$v) {            #transpose discarding nulls
                return array_filter($v); #transpose discarding nulls
            },                           #transpose discarding nulls
            ...$rows                     #transpose discarding nulls
        )                                #unpack
    )                                    #flatten the unpacked elements
);

And here is the PHP7.4 version: (Demo)

var_export(
    array_merge(                           #flatten the unpacked elements
        ...array_map(                      #unpack
            fn(...$v) => array_filter($v), #transpose discarding nulls
            ...$rows                       #transpose discarding nulls
        )                                  #unpack
    )                                      #flatten the unpacked elements
);

Which condenses as:

var_export(array_merge(...array_map(fn(...$v) => array_filter($v), ...$rows)));
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top