Gnuffo1
Gnuffo1

Reputation: 3546

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

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

Upvotes: 18

Views: 28587

Answers (4)

mickmackusa
mickmackusa

Reputation: 47874

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

Upvotes: 4

Mark Elliot
Mark Elliot

Reputation: 77034

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

Upvotes: 10

user244724
user244724

Reputation: 7

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

Upvotes: 0

chetan singhal
chetan singhal

Reputation: 948

function flip_row_col_array($array) {
    $out = array();
    foreach ($array as  $rowkey => $row) {
        foreach($row as $colkey => $col){
            $out[$colkey][$rowkey]=$col;
        }
    }
    return $out;
}

Upvotes: 3

Related Questions