简体   繁体   中英

Transpose and flatten two-dimensional indexed array where rows may not be of equal length

I would like to take an array like this and combine it into 1 single array.

array (size=2)
   0 => 
      array (size=10)
         0 => string '1' 
         1 => string 'a' 
         2 => string '3' 
         3 => string 'c' 
   1 => 
      array (size=5)
         0 => string '2'
         1 => string 'b'

However I want the array results to be interleaved.

So it would end up looking like

array
     0 => '1'
     1 => '2'
     2 => 'a'
     3 => 'b'
     4 => '3'
     5 => 'c'

I would like it so that it doesn't matter how many initial keys are passed in (this one has 2), it should work with 1, 2 or 5. Also, as you can see from my example the amount of elements most likely won't match.

Anyone know the best way to accomplish this?

$data = array(
    0 => array(
        0 => '1',
        1 => 'a',
        2 => '3',
        3 => 'c',
    ),
    1 => array(
        0 => '2',
        1 => 'b',
    ),
);

$newArray = array();
$mi = new MultipleIterator(MultipleIterator::MIT_NEED_ANY);
$mi->attachIterator(new ArrayIterator($data[0]));
$mi->attachIterator(new ArrayIterator($data[1]));
foreach($mi as $details) {
    $newArray = array_merge(
        $newArray,
        array_filter($details)
    );
}
var_dump($newArray);

Tried to think of a fun solution:

$array = [
    ["a","b","c"],
    ["d","e"]
];
$result = [];
while($array) { 
    array_walk(
        $array, 
        function(&$subarray, $key) use (&$array, &$result) { 
            $result[] = array_shift($subarray); 
            if(empty($subarray)) unset ($array[$key]); 
        }
    );
}

var_dump($result);

It destroys the original array though.

I had fun with this... So if you like it use it!

$arr1 = [1,'a',3,'c'];
$arr2 = ['2','b'];

$finarry = arrayInterweave($arr1,$arr2);
print_r($finarry);

function arrayInterweave($arr1,$arr2){
  $count1 = count($arr1);
  $count2 = count($arr2);
  $length = (($count1 >= $count2) ? $count1 : $count2);

  $fin = array();
  for($i = 0;$i<$length;$i++){
      if(!empty($arr1[$i])){
        $fin[] = $arr1[$i];
      }
      if(!empty($arr2[$i])){
        $fin[] = $arr2[$i];
      }
  }
  return $fin;
}

After determining which row contains the most elements, you can loop through known indexes and push columns of data into the result array.

The following technique is safe to use with a variable number of rows.

Code: ( Demo )

$maxCount = max(array_map('count', $array));
$result = [];
for ($i = 0; $i < $maxCount; ++$i) {
    array_push($result, ...array_column($array, $i));
}
var_export($result);

Input/Output:

$array $result
[['b', 'e', 'd', 's'], ['l', 'n']] ['b', 'l', 'e', 'n', 'd', 's']
['f', 'g', 'n', 's'], ['r', 'm'], ['a', 'e', 't'] ['f', 'r', 'a', 'g', 'm', 'e', 'n', 't' 's']

The above technique is perfectly capable of accommodating 3 or more input arrays as well .


ps For anyone running into technical limitations because their php version, this will do the same:

$maxCount = max(array_map('count', $array));
$result = [];
for ($i = 0; $i < $maxCount; ++$i) {
    foreach (array_column($array, $i) as $found) {
        $result[] = $found;
    }
}

...if your php version doesn't accommodate the above snippet, you really, really need to upgrade your php version (sorry, not sorry).


To avoid the counting to determine the longest subarray, you can instead transpose the data with nested loops then flatten that result structure. ( Demo )

$result = [];
foreach ($array as $i => $row) {
    foreach ($row as $k => $v) {
        $result[$k][$i] = $v;
    }
}
 var_export(array_merge(...$result));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM