26

Is it possible to easily 'rotate' an array in PHP?

Like this: 1, 2, 3, 4 -> 2, 3 ,4 ,1

Is there some kind of built-in PHP function for this?

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Dylan
  • 9,129
  • 20
  • 96
  • 153

15 Answers15

33
  $numbers = array(1,2,3,4);
  array_push($numbers, array_shift($numbers));
  print_r($numbers);

Output

Array
(
    [0] => 2
    [1] => 3
    [2] => 4
    [3] => 1
)
Wh1T3h4Ck5
  • 8,399
  • 9
  • 59
  • 79
  • This is fine if you're just using your array like a vector, and the index values are not important. But if you have an associative array that you're trying to rotate, this method wrecks your array keys. See my answer for a way that will preserve them. – Cam Jackson Oct 26 '11 at 22:47
  • @Cam, you're absolutely right about that, even OP haven't mentioned array index, just values. Your answer is valuable for people who are looking for solution to rotate both parts of array elements. (+1 for your answer) – Wh1T3h4Ck5 Oct 29 '11 at 06:46
  • yes, clearly your method was sufficient for the OP, otherwise he wouldn't have accepted it! But yeah, thought I'd add my answer in case anyone had the same problem that I did :) – Cam Jackson Oct 30 '11 at 07:44
  • to rotate right: `array_unshift($numbers,array_pop($numbers));` – Titus Sep 26 '18 at 11:56
28

Most of the current answers are correct, but only if you don't care about your indices:

$arr = array('foo' => 'bar', 'baz' => 'qux', 'wibble' => 'wobble');
array_push($arr, array_shift($arr));
print_r($arr);

Output:

Array
(
    [baz] => qux
    [wibble] => wobble
    [0] => bar
)

To preserve your indices you can do something like:

$arr = array('foo' => 'bar', 'baz' => 'qux', 'wibble' => 'wobble');

$keys = array_keys($arr);
$val = $arr[$keys[0]];
unset($arr[$keys[0]]);
$arr[$keys[0]] = $val;

print_r($arr);

Output:

Array
(
    [baz] => qux
    [wibble] => wobble
    [foo] => bar
)

Perhaps someone can do the rotation more succinctly than my four-line method, but this works anyway.

Cam Jackson
  • 11,860
  • 8
  • 45
  • 78
  • 1
    golfed: `list($k,$v)=each($arr);unset($arr[$k]);$arr[$k]=$v;`. You may have to prepend `reset($arr);`. – Titus Sep 26 '18 at 11:54
  • And of course, you'd need to tolerate that [`each()` has been deprecated](https://www.php.net/manual/en/function.each.php). If you are golfing, then array destructuring braces are shorter than `list()` and `$arr` would be `$a`. – mickmackusa Jul 12 '22 at 22:42
6

It's very simple and could be done in many ways. Example:

$array   = array( 'a', 'b', 'c' );
$array[] = array_shift( $array );
bruno.zambiazi
  • 1,422
  • 14
  • 19
4

Looping through the array, and shift-ing and push-ing, may be a common way to rotate an array, however it can often mess up your keys. A more robust method is using a combination of array_merge and array_splice.

/**
 * Rotates an array.
 * 
 * Numerical indexes will be renumbered automatically.
 * Associations will be kept for keys which are strings.
 * 
 * Rotations will always occur similar to shift and push,
 * where the number of items denoted by the distance are
 * removed from the start of the array and are appended.
 * 
 * Negative distances work in reverse, and are similar to
 * pop and unshift instead.
 * 
 * Distance magnitudes greater than the length of the array
 * can be interpreted as rotating an array more than a full
 * rotation. This will be reduced to calculate the remaining
 * rotation after all full rotations.
 * 
 * @param array $array The original array to rotate.
 * Passing a reference may cause the original array to be truncated.
 * @param int $distance The number of elements to move to the end.
 * Distance is automatically interpreted as an integer.
 * @return array The modified array.
 */
function array_rotate($array, $distance = 1) {
    settype($array, 'array');
    $distance %= count($array);
    return array_merge(
        array_splice($array, $distance), // Last elements  - moved to the start
        $array                          //  First elements - appended to the end
    );
}
// Example rotating an array 180°.
$rotated_180 = array_rotate($array, count($array) / 2);

Alternatively, if you also find the need to rotate keys so that they match with different values, you can combine array_keys, array_combine, array_rotate, and array_values.

/**
 * Rotates the keys of an array while keeping values in the same order.
 * 
 * @see array_rotate(); for function arguments and output.
 */
function array_rotate_key($array, $distance = 1) {
    $keys = array_keys((array)$array);
    return array_combine(
        array_rotate($keys, $distance), // Rotated keys
        array_values((array)$array)    //  Values
    );
}

Or alternatively rotating the values while keeping the keys in the same order (equivalent to calling the negative distance on the matching array_rotate_key function call).

/**
 * Rotates the values of an array while keeping keys in the same order.
 * 
 * @see array_rotate(); for function arguments and output.
 */
function array_rotate_value($array, $distance = 1) {
    $values = array_values((array)$array);
    return array_combine(
        array_keys((array)$array),        // Keys
        array_rotate($values, $distance) //  Rotated values
    );
}

And finally, if you want to prevent renumbering of numerical indexes.

/**
 * Rotates an array while keeping all key and value association.
 * 
 * @see array_rotate(); for function arguments and output.
 */
function array_rotate_assoc($array, $distance = 1) {
    $keys = array_keys((array)$array);
    $values = array_values((array)$array);
    return array_combine(
        array_rotate($keys, $distance),   // Rotated keys
        array_rotate($values, $distance) //  Rotated values
    );
}

It could be beneficial to perform some benchmark tests, however, I expect that a small handful of rotations per request wouldn't affect performance noticeably regardless of which method is used.

It should also be possible to rotate an array by using a custom sorting function, but it would most likely be overly complicated. i.e. usort.

Shaun Cockerill
  • 800
  • 8
  • 11
3

A method to maintain keys and rotate. using the same concept as array_push(array, array_shift(array)), instead we will use array_merge of 2 array_slices

$x = array("a" => 1, "b" => 2, "c" => 3, 'd' => 4);

To move the First element to the end

array_merge(array_slice($x, 1, NULL, true), array_slice($x, 0, 1, true) //'b'=>2, 'c'=>3, 'd'=>4, 'a'=>1

To move the last element to the front

array_merge(array_slice($x, count($x) -1, 1, true), array_slice($x, 0, //'d'=>4, 'a'=>1, 'b'=>2, 'c'=>3

Nick Bisby
  • 1,499
  • 1
  • 9
  • 7
2

you can use this function:

    function arr_rotate(&$array,$rotate_count) {
        for ($i = 0; $i < $rotate_count; $i++) {
            array_push($array, array_shift($array));
        }
    }

usage:

    $xarr = array('1','2','3','4','5');
    arr_rotate($xarr, 2);
    print_r($xarr);

result:

 Array ( [0] => 3 [1] => 4 [2] => 5 [3] => 1 [4] => 2 )
A1Gard
  • 4,070
  • 4
  • 31
  • 55
  • 1
    Nice succinct function, but I feel it can be improved slightly by ensuring you're not looping through more times than the array has elements. For example `arr_rotate($xarr, count($xarr));` will have the results in the same order. Adding the line `$rotate_count %= count($array);` will ensure that the maximum number of iterations is always less than the number of elements. – Shaun Cockerill Mar 02 '18 at 06:49
2

There's a task about array rotation on Hackerrank: https://www.hackerrank.com/challenges/array-left-rotation/problem.

And proposed solution with array_push and array_shift will work for all test cases except the last one, which fails due to timeout. So, array_push and array_shift will give you not the fastest solution.

Here's the faster approach:

function leftRotation(array $array, $n) {
   for ($i = 0; $i < $n; $i++) {
       $value = array[$i]; unset(array[$i]); array[] = $value;
   }
   return array;
}
Gino Pane
  • 4,740
  • 5
  • 31
  • 46
1
$daynamesArray = array("Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday");
array_push($daynamesArray, array_shift($daynamesArray)); //shift by one
array_push($daynamesArray, array_shift($daynamesArray)); //shift by two
print_r($daynamesArray);

The output starts at "Wednesday":

Array ( [0] => Wednesday [1] => Thursday [2] => Friday [3] => Saturday [4] => Sunday [5] => Monday [6] => Tuesday 
bummi
  • 27,123
  • 14
  • 62
  • 101
Mikeys4u
  • 1,494
  • 18
  • 26
1

Use array_shift and array_push.

Matt Ball
  • 354,903
  • 100
  • 647
  • 710
1

Yes it is, here is a function I did myself, where $A is the array and $K the number of times you want to rotate the array:

function solution($A, $K) {

  for($i = 0; $i < $K; $i++): //we cycle $K
    $arrayTemp = $A;
    for($j = 0; $j < count($arrayTemp); $j++): // we cycle the array
       if($j == count($arrayTemp) - 1) $A[0] = $arrayTemp[$j]; // we check for the last position
       else $A[$j + 1] = $arrayTemp[$j]; // all but last position
    endfor;
  endfor;
 return $A;

}
Josep Vidal
  • 2,580
  • 2
  • 16
  • 27
0

The logic is to swap the elements. Algorithm may look like -

 for i = 0 to arrayLength - 1
    swap( array[i], array[i+1] )     // Now array[i] has array[i+1] value and 
                                     // array[i+1] has array[i] value.
Mahesh
  • 34,573
  • 20
  • 89
  • 115
0

No. Check the documentation for array_shift and its related functions for some tools you can use to write one. There might even be an array_rotate function implemented in the comments of that page.

It's also worth reading through the array functions listed on the left-hand sidebar to get a full understanding of what array functions are available in PHP.

intuited
  • 23,174
  • 7
  • 66
  • 88
0

Here's a function to rotate an array (zero-indexed array) to any position you want:

function rotateArray($inputArray, $rotateIndex) {
  if(isset($inputArray[$rotateIndex])) {
    $startSlice = array_slice($inputArray, 0, $rotateIndex);
    $endSlice = array_slice($inputArray, $rotateIndex);
    return array_merge($endSlice, $startSlice);
  }
  return $inputArray;
}

$testArray = [1,2,3,4,5,6];
$testRotates = [3, 5, 0, 101, -5];

foreach($testRotates as $rotateIndex) {
  print_r(rotateArray($testArray, $rotateIndex));
}
Harry Sadler
  • 326
  • 3
  • 7
0

Here's the core solution: (Shift even if shiftBy is greater than array length)

function rotate($array, $shiftBy)
{
    $res = [];
    $c   = count($array);
    for ($i = 0; $i < $c; $i++) {
        $n       = ($i + $shiftBy) % $c; // mod will take care of positions if rotates
        $res[$n] = $array[$i];
    }
    ksort($res);

    return $res;
}

Burn this, you will get it.

Rahul
  • 18,271
  • 7
  • 41
  • 60
-1

Not too dissimilar to the first snippet in ShaunCockerill's answer, I also endorse not making iterated functions calls to perform the rotation. In fact, I'll recommend using early returns to optimize performance and reduce the total number of function calls needed.

The following snippet is the "move left" version of the "move right" version that I posted here. In my demo, there is a single, static input array and the foreach() loop merely changes the desired amount of rotation (0 to 9).

Code: (Demo)

function shiftPop(array $indexedArray, int $shiftPopsCount): array
{
    $count = count($indexedArray);
    if ($count < 2) {
        return $indexedArray;
    }
    $remainder = $shiftPopsCount % $count;
    if (!$remainder) {
        return $indexedArray;
    }
    return array_merge(
        array_splice($indexedArray, $remainder),
        $indexedArray
    );
}

$array = [1, 2, 3, 4];
foreach (range(0, 9) as $moves) {
    var_export(shiftPop($array, $moves));
    echo "\n---\n";
}

The first if block in my snippet is not engaged by my demo because my array always has 4 elements. The second if block is engaged, when $moves is 0, 4, and 8 -- in these cases, the input is identical to the desired output, so calling array_merge() and array_splice() is pointless.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
  • So this answer was dv'ed for demonstrating and explaining the approach with the lowest time complexity on the page? Sure, that makes sense on a knowledge sharing site. – mickmackusa Jun 20 '22 at 20:58