2

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,....));
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
JuKe
  • 663
  • 2
  • 7
  • 20
  • 2
    What happens if the arrays are of different sizes, or their keys are not consecutively numbered from `0`? – eggyal Oct 20 '13 at 15:16
  • the arrays can have differenz sizes... the merging shoud still go on.. – JuKe Oct 20 '13 at 15:18
  • 1
    Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, and why they didn't work. – Kermit Oct 20 '13 at 15:19
  • But go on in what way? Suppose `$b = ['b1','b2'];` in your example above; should the resulting array end with `[...,'a3','b1','c3']` (i.e. with array `$b` going back to the start), or should the "b" position be null, or should it contain some default value, or should it be omitted entirely? You need to define your requirements more explicitly. – eggyal Oct 20 '13 at 15:20
  • will the keys of all arrays be consecutively numbered from 0? – Praveen B Oct 20 '13 at 15:21
  • @JuKe, Do you want to preserve the keys ? – Shankar Narayana Damodaran Oct 20 '13 at 15:34
  • @ShankarDamodaran: Key preservation raises further questions of how collisions should be handled. – eggyal Oct 20 '13 at 15:36
  • @eggyal: the arrays all start with 0 and all keys are numeric... – JuKe Oct 20 '13 at 15:37
  • 1
    @JuKe: But are they *guaranteed* to be keyed consecutively? Or might one array have a missing element, e.g. only contain keys 1 and 3? – eggyal Oct 20 '13 at 15:40
  • @eggyal: i my example the keys a consecutively! – JuKe Oct 20 '13 at 15:45
  • Then everything will be okay provided you only ever use your example data. – eggyal Oct 20 '13 at 15:45
  • [An approach that doesn't rely on `array_filter()`](https://stackoverflow.com/a/68977453/2943403) – mickmackusa Sep 05 '22 at 01:59

9 Answers9

1

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, ...);
CreatoR
  • 1,654
  • 10
  • 14
  • +1 Very nice solution. I'd only add emphasis that colliding non-numeric keys will get overridden, however it's easy to see how that can be altered if so desired. – eggyal Oct 20 '13 at 15:53
  • You might also like to [`reset()`](http://www.php.net/manual/en/function.reset.php) the array cursors before commencing, just in case. – eggyal Oct 20 '13 at 15:54
1

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',
)
salathe
  • 51,324
  • 12
  • 104
  • 132
  • and how is the opinion to do this with more than 3 arrays, except to write `array_map(null, $a, $b, $c, $d)` – JuKe Oct 20 '13 at 16:00
  • @JuKe yes, just add more arrays to the `array_map` call. – salathe Oct 20 '13 at 16:01
  • @eggyal try it and see. – salathe Oct 20 '13 at 16:02
  • @salathe: i wrote ... "except to write `array_map(null, $a, $b, $c, $d)`" there are various number of arrays – JuKe Oct 20 '13 at 16:04
  • @JuKe you mean, the number of arrays is not fixed? – salathe Oct 20 '13 at 16:05
  • Apologies, I hadn't noticed that the OP's edited example contained revised data. – eggyal Oct 20 '13 at 16:05
  • 1
    @JuKe First, please add that more clearly to the question (the "opinion" part is confusing, did you mean "option"?). Secondly, how many arrays there are does not affect the method used above, of merging the arrays. You can call `array_map()` dynamically (e.g. with `call_user_func_array()`) if you need to. – salathe Oct 20 '13 at 16:07
  • @salathe: sorry for the misunderstanding. can you update your post with the dynamic of call_user_func_array() ? – JuKe Oct 20 '13 at 16:14
  • @JuKe: If your arrays are currently stored in separate variables, then at some point you're always going to have to list those variables in your code. Doing that within the arguments of a call to `array_map()` is no more work than doing it in the construction of an array to pass to `call_user_func_array()`. If, however, the arrays are obtained from elsewhere (whether they are read from some external source, or are the fields of some object, or are themselves elements of some array) then that is another matter altogether and would indeed admit for a more flexible approach. Which is it? – eggyal Oct 20 '13 at 16:34
1

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
)
alexg
  • 902
  • 11
  • 37
0

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 )
Shankar Narayana Damodaran
  • 68,075
  • 43
  • 96
  • 126
  • if i uses array_merge, i got a result like this: Array ( [0] => a1 [1] => a2 [2] => a3 [3] => b1 [4] => b2 [5] => b3 [6] => c1 [7] => c2 [8] => c3 ) – JuKe Oct 20 '13 at 15:13
  • Yep, okay. This seems to meet the minimum requirements, so I have removed my downvote; although it still doesn't address what should happen if the arrays are not keyed naturally. – eggyal Oct 20 '13 at 15:45
  • But OP mentioned that `the arrays all start with 0 and all keys are numeric.` Actually i am not getting what you are trying to say eggyal. Can you be a bit precise ? – Shankar Narayana Damodaran Oct 20 '13 at 15:53
  • @ShankarDamodaran: the focus should be on efficiency.. you run the array twice... – JuKe Oct 20 '13 at 16:21
  • What I was trying to illustrate was the hazard that arises if, say, array `$a[2]` was in fact keyed as `$a[10]` (but there were no additional elements in the array): in this case, your counter `$i` will never reach the value of 10 (because its limiting `$val` is set to the size of the largest array, still 5); therefore that element will never make it into the result. – eggyal Oct 20 '13 at 16:25
  • Furthermore, you face similar hazards if some keys are not numeric, e.g. if `$b[1]` was keyed as `$b['one']` - albeit the OP has excluded this possibility in subsequent comments. – eggyal Oct 20 '13 at 16:26
0

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.

Community
  • 1
  • 1
nurakantech
  • 502
  • 4
  • 14
  • -1 "[*the arrays can have differenz sizes... the merging shoud still go on..*](http://stackoverflow.com/questions/19478981/how-can-i-merge-arrays-like-a-gearwheel#comment28888956_19478981)". Also what happens if each array is not keyed numerically with consecutive numbers from 0? – eggyal Oct 20 '13 at 15:25
  • why don't you try merging all the arrays and sort them. – nurakantech Oct 20 '13 at 15:37
  • I'm not sure I understand what that means? How do you propose to undertake the sort? – eggyal Oct 20 '13 at 15:47
  • Thanks for the edit, but you will notice that your results now are not in the order desired by the OP. Specifically, the sort happens to have placed the elements that were originally array `$a` before those that were originally in `$b` etc. Furthermore, it is dependent on the values, which I am sure is not the desired behaviour. – eggyal Oct 20 '13 at 16:17
0

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));
George Brighton
  • 5,131
  • 9
  • 27
  • 36
  • What happens if each array is not keyed numerically with consecutive numbers from 0? – eggyal Oct 20 '13 at 15:38
  • 1
    If they're not keyed numerically, the OP hasn't specified what they want to happen. If numbers are not consecutive, they need to be made consecutive - which is trivial - before running this code. Given the question, this is a valid solution. Perhaps you could post yours? – George Brighton Oct 20 '13 at 15:39
  • Agreed. With those extra caveats, you get my +1 :) – eggyal Oct 20 '13 at 15:41
0

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

ziollek
  • 1,973
  • 1
  • 12
  • 10
  • This solution is completely dependent on the values involved, which I suspect should be incidental; indeed, I would imagine the values depicted in the question were merely contrived examples to illustrate the desired behaviour. – eggyal Oct 20 '13 at 16:19
  • ok, question isn't clear. I'll edit and add data independent solution – ziollek Oct 20 '13 at 16:58
0

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
)
Tom
  • 691
  • 4
  • 7
  • This solution is completely dependent on the values involved, which I suspect should be incidental; indeed, I would imagine the values depicted in the question were merely contrived examples to illustrate the desired behaviour. – eggyal Oct 20 '13 at 16:45
  • May be so, but the tread starter has explicit asked with these values in mind and never mentioned any other pattern. So i simply gave an answer to that. – Tom Oct 20 '13 at 16:52
0

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)));
mickmackusa
  • 43,625
  • 12
  • 83
  • 136