Reputation: 3546
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
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:
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],
];
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
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
Reputation: 7
here you go. It works. :)
$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
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