Task:
Write a function which rotates all elements of a given array to the right.
Example: [1, 2, 3] => [3, 1, 2]
My solution:
func rotateRight(array ary: [Int]) -> [Int] {
guard ary.isEmpty == false else {
return ary
}
var copy = ary
let last = ary.last
var i = ary.count - 1
while i > 0 {
copy[i] = copy[i - 1]
i -= 1
}
copy[0] = last!
return copy
}
var ary1 = [1, 2, 3]
print(rotateRight(array: ary1)) // [3, 1, 2]
var ary2 = [1, 1, 2, 3, 3, 4]
print(rotateRight(array: ary2)) // [4, 1, 1, 2, 3, 3]
var ary3 = [Int]()
print(rotateRight(array: ary3)) // []
var ary4 = [1]
print(rotateRight(array: ary4)) // [1]
I have to make a copy of the array because Swift-parameter are immutable. Do I handle the issue in a proper way? Or should it be done differently?
Is there a better way to solve the described task?
1 Answer 1
A comparison against true
or false
is never needed, ary.isEmpty == false
is more concisely expressed as !ary.isEmpty
.
Here I would not use a guard
statement at all. That statement was introduced to get rid of the "if-let pyramid of doom," to unwrap a variable without introducing another scope/indentation level. But I would not use a guard
statement for every "early return" situation, in particular not if it makes the statement look like a double negation.
if ary.isEmpty {
return ary
}
is shorter and clearer in my opinion. Alternatively one can use optional binding
if let last = ary.last {
// ...
} else {
return ary
}
to test for a non-empty array and get the last element in one step, that allows to get rid of the ugly forced unwrap copy[0] = last!
in your code.
As already mentioned in the comment, array slicing methods can be used instead of copying all elements to their new place:
if let last = ary.last {
return [last] + ary.dropLast()
} else {
return ary
}
Another option is to concatenate two array slices, this can easily be generalized to shifting array elements by more than one position to the right or left:
if ary.isEmpty {
return ary
}
let frontIndex = ary.count - 1
return Array(ary[frontIndex...]) + ary[0..<frontIndex]
Naming: The parameter name ary
reminds my of unary/binary/arity and is not significantly shorter than array
. My suggestion is to use array
with an empty argument label:
func rotateRight(_ array: [Int]) -> [Int] {
// ...
}
var array1 = [1, 2, 3]
print(rotateRight(array1)) // [3, 1, 2]
Of course, if you don't want to reinvent the wheel then you can simply use the rotate(toStartAt:)
method of the swift-algorithms package.
guard let last = ary.last else { return ary }; return [last] + ary.dropLast()
\$\endgroup\$return ary.isEmpty ? ary : ary.suffix(1) + ary.prefix(ary.count - 1)
\$\endgroup\$