4

I am solving a larger problem and at one step I need to rotate a 2D array counter-clockwise.

So if I have this matrix:

1 2 3 4
1 2 3 4
3 4 5 6
3 4 5 6

After the rotation it will be:

4 4 6 6
3 3 5 5
2 2 4 4
1 1 3 3

I have found a solution to rotate it clockwise:

<?php    
$a = array(array(1,2,3,4),array(5,6,7,8),array(9,0,1,2),array(3,4,5,6));
$b = array(); //result

while(count($a)>0)
{
    $b[count($a[0])-1][] = array_shift($a[0]);
    if (count($a[0])==0)
    {
         array_shift($a);
    }
}
?>

The thing is that this has to work even when a is uni-dimensional or has only one element.

So, 1 2 3 4 will become:

4
3
2
1
Community
  • 1
  • 1
XCS
  • 27,244
  • 26
  • 101
  • 151

4 Answers4

12
$b = call_user_func_array(
    'array_map',
    array(-1 => null) + array_map('array_reverse', $a)
);

I'll leave it as an exercise for the reader to figure out how it works.

salathe
  • 51,324
  • 12
  • 104
  • 132
  • I tried to figure out why the `array_map` with the `-1 => null` function (or what actually the function is) groups nth elements from each row... http://ideone.com/ES75XJ Can you please explain that? – XCS Jun 08 '13 at 16:00
  • 2
    @Cristy see the example "[Creating an array of arrays](http://php.net/manual/en/function.array-map.php#example-4844)" in the manual. Your code essentially calls `array_map(null, [2,1], [3,2], [4,3])` which returns `[[2,3,4],[1,2,3]]`. – salathe Jun 08 '13 at 16:07
  • 2
    Interesting method! You can replace `array_map('array_reverse', $a)` with `$a` if you want to just rotate it clockwise, too. – Matt Fletcher Sep 12 '13 at 15:13
  • 1
    @MattFletcher actually, replacing `array_map('array_reverse', $a)` with `$a` will rotate clockwise, but also reflect along the y axis. To only rotate CW, replace `array_map('array_reverse', $a)` with `array_reverse($a)`. – Benjam Mar 26 '15 at 04:46
2

Most concisely, transpose the array then reverse the first level. Done.

Code: (Demo)

$a = [[1,2,3,4],[1,2,3,4],[3,4,5,6],[3,4,5,6]];

var_export(array_reverse(array_map(null, ...$a)));

This approach will fail if the input array is a rectangular matrix with only one row. This is due to how array_map() behaves with a null callback.

To stabilize the transposition, use nested loops (Demo)

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

Or (Demo)

$result = [];
foreach ($array as $row) {
    foreach (array_reverse($row) as $i => $v) {
        $result[$i][] = $v;
    }
}
var_export($result);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
0

Here's a solution that does both clockwise and counter-clockwise matrix rotation:

$a = array(array(1,2,3,4),array(5,6,7,8),array(9,0,1,2),array(3,4,5,6));
$b = $a; //result

$clockwise = false;  // toggle for clockwise / counter-clockwise

$rows = count($a);
$columns = ($rows > 0) ? count($a[0]) : 0;

for ($y = 0; $y < $rows; $y++) {
  for ($x = 0; $x < $columns; $x++) {
    $newX = $clockwise ? $y                  : ($rows - 1) - $y;
    $newY = $clockwise ? ($columns - 1) - $x : $x;
    $b[$newX][$newY] = $a[$x][$y];
  }
}
Filippos Karapetis
  • 4,367
  • 21
  • 39
0

Here is a recursive way:

            $m = array();
            $m[0] = array('a', 'b', 'c');
            $m[1] = array('d', 'e', 'f');
            $m[2] = array('g', 'h', 'i');
            $newMatrix = array();

            function rotateMatrix($m, $i = 0, &$newMatrix)
            {
                foreach ($m as $chunk) {
                    $newChunk[] = $chunk[$i];
                }
                $newMatrix[] = array_reverse($newChunk);
                $i++;

                if ($i < count($m)) {
                    rotateMatrix($m, $i, $newMatrix);
                }
            }

            rotateMatrix($m, 0, $newMatrix);
            echo '<pre>';
            var_dump($newMatrix);
            echo '<pre>';
taxicala
  • 21,408
  • 7
  • 37
  • 66