16

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;
}
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Gnuffo1
  • 3,478
  • 11
  • 39
  • 53

4 Answers4

11

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;
        }
    }
}
Dharman
  • 30,962
  • 25
  • 85
  • 135
Mark Elliot
  • 75,278
  • 22
  • 140
  • 160
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;
}
chetan singhal
  • 948
  • 1
  • 13
  • 36
  • 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: https://3v4l.org/SAm2f – mickmackusa Dec 08 '20 at 21:47
  • 1
    OP said they don't want this. – Dharman Dec 09 '20 at 00:47
2

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);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
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);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
  • 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. – mickmackusa Dec 08 '20 at 22:12
  • 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. – mickmackusa Dec 09 '20 at 02:15