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:
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])
}
}
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.
shift() Shift is destructive. It removes the first element of an array, and returns the removed element.
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()
.
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: []
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With