Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Shift elements in array by index

Given array of n elements, i.e.

var array = [1, 2, 3, 4, 5]

I can write an extension to the Array so I can modify array to achieve this output: [2, 3, 4, 5, 1]:

mutating func shiftRight() {
  append(removeFirst())
}

Is there a way to implement such a function that would shift array by any index, positive or negative. I can implement this function in imperative style with if-else clauses, but what I am looking for is functional implementation.

The algorithm is simple:

  1. Split array into two by the index provided
  2. append first array to the end of the second

Is there any way to implement it in functional style?

The code I've finished with:

extension Array {
  mutating func shift(var amount: Int) {
    guard -count...count ~= amount else { return }
    if amount < 0 { amount += count }
    self = Array(self[amount ..< count] + self[0 ..< amount])
  }
}
like image 308
Richard Topchii Avatar asked Oct 21 '15 16:10

Richard Topchii


People also ask

How do you shift elements in an array?

Create a temp variable and assign the value of the original position to it. Now, assign the value in the new position to original position. Finally, assign the value in the temp to the new position.

Is shift () destructive?

shift() Shift is destructive. It removes the first element of an array, and returns the removed element.


2 Answers

You can use ranged subscripting and concatenate the results. This will give you what you're looking for, with names similar to the standard library:

extension Array {
    func shiftRight(var amount: Int = 1) -> [Element] {
        guard count > 0 else { return self }
        assert(-count...count ~= amount, "Shift amount out of bounds")
        if amount < 0 { amount += count }  // this needs to be >= 0
        return Array(self[amount ..< count] + self[0 ..< amount])
    }

    mutating func shiftRightInPlace(amount: Int = 1) {
        self = shiftRight(amount)
    }
}

Array(1...10).shiftRight()
// [2, 3, 4, 5, 6, 7, 8, 9, 10, 1]
Array(1...10).shiftRight(7)
// [8, 9, 10, 1, 2, 3, 4, 5, 6, 7]

Instead of subscripting, you could also return Array(suffix(count - amount) + prefix(amount)) from shiftRight().

like image 120
Nate Cook Avatar answered Oct 20 '22 16:10

Nate Cook


With Swift 5, you can create shift(withDistance:) and shiftInPlace(withDistance:) methods in an Array extension with the following implementation in order to solve your problem:

extension Array {

    /**
     Returns a new array with the first elements up to specified distance being shifted to the end of the collection. If the distance is negative, returns a new array with the last elements up to the specified absolute distance being shifted to the beginning of the collection.

     If the absolute distance exceeds the number of elements in the array, the elements are not shifted.
     */
    func shift(withDistance distance: Int = 1) -> Array<Element> {
        let offsetIndex = distance >= 0 ?
            self.index(startIndex, offsetBy: distance, limitedBy: endIndex) :
            self.index(endIndex, offsetBy: distance, limitedBy: startIndex)

        guard let index = offsetIndex else { return self }
        return Array(self[index ..< endIndex] + self[startIndex ..< index])
    }

    /**
     Shifts the first elements up to specified distance to the end of the array. If the distance is negative, shifts the last elements up to the specified absolute distance to the beginning of the array.

     If the absolute distance exceeds the number of elements in the array, the elements are not shifted.
     */
    mutating func shiftInPlace(withDistance distance: Int = 1) {
        self = shift(withDistance: distance)
    }

}

Usage:

let array = Array(1...10)
let newArray = array.shift(withDistance: 3)
print(newArray) // prints: [4, 5, 6, 7, 8, 9, 10, 1, 2, 3]
var array = Array(1...10)
array.shiftInPlace(withDistance: -2)
print(array) // prints: [9, 10, 1, 2, 3, 4, 5, 6, 7, 8]
let array = Array(1...10)
let newArray = array.shift(withDistance: 30)
print(newArray) // prints: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let array = Array(1...10)
let newArray = array.shift(withDistance: 0)
print(newArray) // prints: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var array = Array(1...10)
array.shiftInPlace()
print(array) // prints: [2, 3, 4, 5, 6, 7, 8, 9, 10, 1]
var array = [Int]()
array.shiftInPlace(withDistance: -2)
print(array) // prints: []
like image 26
Imanou Petit Avatar answered Oct 20 '22 14:10

Imanou Petit