4
\$\begingroup\$

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?

toolic
14.5k5 gold badges29 silver badges203 bronze badges
asked Dec 28, 2024 at 7:12
\$\endgroup\$
2
  • 1
    \$\begingroup\$ guard let last = ary.last else { return ary }; return [last] + ary.dropLast() \$\endgroup\$ Commented Dec 28, 2024 at 18:53
  • 1
    \$\begingroup\$ return ary.isEmpty ? ary : ary.suffix(1) + ary.prefix(ary.count - 1) \$\endgroup\$ Commented Dec 28, 2024 at 18:55

1 Answer 1

2
\$\begingroup\$

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.

answered Dec 29, 2024 at 14:06
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.