Flip (transpose) the rows and columns of a 2D array without changing the number of columns
Asked Answered
S

4

18

Normally, I'd be asking how to turn a 4-rowed, 3-columned array like this:

1      2        3
4      5        6
7      8        9
10    11       12

Into a 3-rowed, 4-columned array like: (I DON'T WANT THIS)

1   4   7   10
2   5   8   11
3   6   9   12

But actually, I want to turn it into this: (I WANT THIS)

1   5   9
2   6   10
3   7   11
4   8   12

In other words, I want to flip the rows and columns, but keep the same "width" and "height" of the new array. I've been stuck on this for over an hour.

This is the function I'm using to do a normal "flip" (the first example):

function flip($arr)
{
    $out = array();
    foreach ($arr as $key => $subarr)
    {
        foreach ($subarr as $subkey => $subvalue)
        {
            $out[$subkey][$key] = $subvalue;
        }
    }
    return $out;
}
Spear answered 8/2, 2010 at 12:21 Comment(4)
What about array_flip? Won't this do?Celanese
No, because that flips the keys and values of a 1d array. This is completely different.Spear
simple switching rows into columns can be made using array_column() functionPsychophysics
array_column() is php >= 5.5Springbok
K
10

Just walk the array in the correct order. Assuming you have relatively small arrays, the easiest solution is just to create a brand new array during that walk.

A solution will be of the form:

$rows = count($arr);
$ridx = 0;
$cidx = 0;

$out = array();

foreach($arr as $rowidx => $row){
    foreach($row as $colidx => $val){
        $out[$ridx][$cidx] = $val;
        $ridx++;
        if($ridx >= $rows){
            $cidx++;
            $ridx = 0;
        }
    }
}
Krupp answered 8/2, 2010 at 12:27 Comment(0)
O
4

First, what you don't want (which is half of the solution to what you do want)...

The term for "flipping rows and columns" is "transposing".

PHP has had a sleek native technique for this very action since the splat operator was added to the language.

The caveats to bear in mind are:

  1. All keys must be numeric. The splat/spread operator will choke on non-numeric keys.
  2. The matrix must be complete. If there are any gaps, you may not get the result that you desire.

Code: (Demo)

$matrix = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
    [10, 11, 12],
];

var_export(
    array_map(null, ...$matrix)
);

Output:

[
    [1, 4, 7, 10],
    [2, 5, 8, 11],
    [3, 6, 9, 12],
];

Now, for what you do want!

Here is a functional-style snippet that incorporates php's transposing technique while ensuring that the output has the same number of columns as the input.

Code: (Demo)

var_export(
    array_map(
        null,
        ...array_chunk(
            array_merge(...$matrix),
            count($matrix)
        )
    )
);

Output:

[
    [1, 5, 9],
    [2, 6, 10],
    [3, 7, 11],
    [4, 8, 12],
];

This approach flattens the input, then breaks it into rows with lengths equivalent to the original number of rows, then that result is transposed.


Late Edit: As a purely academic pursuit, I wanted to see what a pure mathematical technique would look like which didn't use any conditions and didn't maintain multiple "counters".

As it turns out, because php arrays will truncate float keys to integers, this can be done in a single loop.

// assuming the earlier mentioned 3x4 matrix:
i    old pos   new pos  i%rows  i/rows  i/col   i%col  
0  : [0][0] => [0][0]     0      0       0        0
1  : [1][0] => [0][1]     1      0.25    0.3      1
2  : [2][0] => [0][2]     2      0.5     0.6      2
3  : [3][0] => [1][0]     3      0.75    1        0
4  : [0][1] => [1][1]     0      1       1.3      1
5  : [1][1] => [1][2]     1      1.25    1.6      2
6  : [2][1] => [2][0]     2      1.5     2        0
7  : [3][1] => [2][1]     3      1.75    2.3      1
8  : [0][2] => [2][2]     0      2       2.6      2
9  : [1][2] => [3][0]     1      2.25    3        0
10 : [2][2] => [3][1]     2      2.5     3.3      1
11 : [3][2] => [3][2]     3      2.75    3.6      2 

Code: (Demo)

$rows = count($matrix);
$cols = count(current($matrix));
$cells = $rows * $cols;

$result = $matrix;  // used to preserve original key orders
for ($i = 0; $i < $cells; ++$i) {
    $result[$i % $rows][$i / $rows] = $matrix[$i / $cols][$i % $cols];
}
var_export($result);
Obsess answered 8/12, 2020 at 10:48 Comment(0)
C
3
function flip_row_col_array($array) {
    $out = array();
    foreach ($array as  $rowkey => $row) {
        foreach($row as $colkey => $col){
            $out[$colkey][$rowkey]=$col;
        }
    }
    return $out;
}
Cope answered 28/2, 2013 at 8:39 Comment(2)
This unexplained answer is exactly what the OP said they were NOT looking for. This is a classic transposing technique (which php already offers a native way of doing this). Proof: 3v4l.org/SAm2fObsess
OP said they don't want this.Arose
C
0

here you go. It works. :)

Demonstration

$input = [
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
    [10, 11, 12],
];

// flipping matrices
$output = array();
$intern = array();

for($row=0; $row < 4; $row++)
    for($col=0;$col < 3;$col++)
        $intern[] = $input[$row][$col];
    
// nesting the array
$count = 0;
$subcount = 0;

foreach($intern as $value)
{

    $output[$count][$subcount] = $value;
    $count++;

    if($subcount == 3)
    {
        break;
    }

    if($count == 4)
    {
        $count = 0;
        $subcount++;
    }
}


echo "\n final output ";print_r($output);
Coenocyte answered 8/2, 2010 at 13:35 Comment(2)
I have fixed the typos in your code. Refreshingly, it runs as desired. Please edit your answer to include an educational explanation which benefits researchers. I would like to UV the rare correct answers on this page, but I never UV unexplained answers.Obsess
On the other hand, this snippet has hardcode/magic number numbers in it (3 and 4), so it will not be instantly portable to other use cases.Obsess

© 2022 - 2024 — McMap. All rights reserved.