Shift Swift Array

I know this might be late. But the easiest way to rotate or shift an array is

func shifter(shiftIndex: Int) {
   let strArr: [String] = ["a","b","c","d"]
   var newArr = strArr[shiftIndex..<strArr.count]
   newArr += strArr[0..<shiftIndex]       
   println(newArr)  }

shifter(2) //[c, d, a, b] you can modify the function to take array as input

Short & clear Swift 3 & 4 solution I came up with:

extension Array {

    func shifted(by shiftAmount: Int) -> Array<Element> {

        // 1
        guard self.count > 0, (shiftAmount % self.count) != 0 else { return self }

        // 2
        let moduloShiftAmount = shiftAmount % self.count
        let negativeShift = shiftAmount < 0
        let effectiveShiftAmount = negativeShift ? moduloShiftAmount + self.count : moduloShiftAmount

        // 3
        let shift: (Int) -> Int = { return $0 + effectiveShiftAmount >= self.count ? $0 + effectiveShiftAmount - self.count : $0 + effectiveShiftAmount }

        // 4
        return self.enumerated().sorted(by: { shift($0.offset) < shift($1.offset) }).map { $0.element }

    }

}

Explanation:

  1. Arrays with no elements and shifts producing the identity of the original array are returned immediately
  2. To get the effective shift amount regardless of the amount passed with the function, we do some modulo calculation to get rid of shifts that would rotate the elements in the array more than once (e.g. in an Array with 5 Objects, a shift of +7 is the same as a shift of +2). Since we always want to shift to the right, in order to be done with one simple function instead of two, negative inputs have to be dealt with (e.g. in an Array with 5 Objects, a shift of -2 is the same as a shift of +3). Therefore we adjust the negative results of the modulo calculation by the length of the array. Of course those 3 lines could be done in one, but I wanted to make this as readable as possible.
  3. Now we prepare the actual shift by taking the index ($0) of element and returning the shifted index by adding the amount calculated in step 2. If the new index lands outside of the array length, it needs to be wrapped around to the front.
  4. And finally we apply all the preparation to our array with some trickery: enumerated() gives us an array of tuples [(offset: Int, element: Int)], which is simply the original index of every element and the element itself. We then sort this enumerated array by the manipulated offset (aka the element's index) via applying the function from step 3. Lastly we get rid of the enumeration by mapping the sorted elements back into an array.

This extension works with arrays of any type. Examples:

let colorArray = [
    UIColor.red,
    UIColor.orange,
    UIColor.yellow,
    UIColor.green,
    UIColor.blue
]

let shiftedColorArray = [
    UIColor.green,
    UIColor.blue,
    UIColor.red,
    UIColor.orange,
    UIColor.yellow
]

colorArray.shifted(by: 2) == shiftedColorArray // returns true

[1,2,3,4,5,6,7].shifted(by: -23) // returns [3,4,5,6,7,1,2]

You can extend Array to include a method to return an array containing the elements of the original array rotated by one element:

extension Array {
    func rotate(shift:Int) -> Array {
        var array = Array()
        if (self.count > 0) {
            array = self
            if (shift > 0) {
                for i in 1...shift {
                    array.append(array.removeAtIndex(0))
                }
            }
            else if (shift < 0) {
                for i in 1...abs(shift) {
                    array.insert(array.removeAtIndex(array.count-1),atIndex:0)
                }
            }
        }
        return array
    }
}

To shifts the elements of an array once

let colorArray:[UIColor] = [
    .redColor(),
    .orangeColor(),
    .yellowColor(),
    .greenColor(),
    .blueColor()
]

let z = colorArray.rotate(1)

// z is [.orangeColor(), .yellowColor(), .greenColor(), .blueColor(), .redColor()]

and twice

let z = colorArray.rotate(2)

// z is [.yellowColor(), .greenColor(), .blueColor(), .redColor(), .orangeColor()]

Tags:

Arrays

Ios

Swift