Array Rotation: A Deep Dive into Techniques in Swift and Use Cases in iOS Apps

Unraveling the Versatility of Array Manipulation for Enhanced iOS User Interfaces

Cong Le
Stackademic

--

Photo by AltumCode on Unsplash

Here are several techniques to rotate an array in Swift:

  1. Slicing and Concatenation:
func rotateArray(_ nums: [Int], _ k: Int) -> [Int] {
let k = k % nums.count
let leftPart = nums[..<k]
let rightPart = nums[k...]
return Array(rightPart + leftPart)
}

2. Using prefix, suffix:

func rotateArray(_ nums: [Int], _ k: Int) -> [Int] {
let k = k % nums.count
let leftPart = nums.suffix(from: k)
let rightPart = nums.prefix(upTo: k)
return Array(leftPart + rightPart)
}

3. Using dropFirst and dropLast:

func rotateArray(_ nums: [Int], _ k: Int) -> [Int] {
let k = k % nums.count
let leftPart = nums.dropFirst(nums.count - k)
let rightPart = nums.dropLast(k)
return Array(leftPart + rightPart)
}

4. In-place Rotation with Reversal:

func rotateArray(_ nums: inout [Int], _ k: Int) {
let k = k % nums.count
reverse(&nums, 0, nums.count - 1)
reverse(&nums, 0, k - 1)
reverse(&nums, k, nums.count - 1)
}

5. Using the Array's rotate method (Swift 5.5 and later):

extension Array {
mutating func rotate(rightBy k: Int) {
let k = k % self.count
self = Array(self[(self.count - k)...] + self[..<self.count - k])
}
}

6. Using Array's append(contentsOf:) and removeFirst(_:):

func rotateArray(_ nums: [Int], _ k: Int) -> [Int] {
var nums = nums
let k = k % nums.count
nums.append(contentsOf: nums[0..<k])
nums.removeFirst(k)
return nums
}

Each of these methods achieves the same result, rotating the array to the right by k steps, but they use different techniques and have different performance characteristics. The choice of method may depend on the specific requirements of your application, such as whether you need to rotate in place or you can return a new array.

A coding example

Rotate Array:
Write a program to rotate the elements of an array **k** steps to the right.
For example, if the array is [1, 2, 3, 4, 5] and the k step is 2 the output should be [4, 5, 1, 2, 3].

We will solve this coding problem using different methods.

Method 1: Slicing and Concatenation

func rotateArray(_ nums: [Int], _ k: Int) -> [Int] {
let k = k % nums.count // Ensure k is within the bounds of the array
let leftPart = nums[nums.count - k..<nums.count] // Get the last 'k' elements
let rightPart = nums[0..<nums.count - k] // Get the elements before the last 'k' elements
return Array(leftPart) + Array(rightPart) // Concatenate the two parts to get the rotated array
}

// Example usage:
let myArray = [1, 2, 3, 4, 5]
let rotatedArray = rotateArray(myArray, 2) // rotatedArray is [4, 5, 1, 2, 3]

print(rotatedArray)

Method 2: Using prefix() and suffix()

// Swift function to rotate an array to the right by k steps using prefix and suffix
func rotateArray(_ nums: [Int], _ k: Int) -> [Int] {
let n = nums.count // Get the count of elements in the array
guard n > 0, k > 0 else { return nums } // If array is empty or k is 0, no rotation is needed
let k = k % n // Use modulus in case k is larger than array size to get the effective rotation step

// Obtain the last 'k' elements as the new prefix and the rest as the new suffix
let newPrefix = nums.suffix(k) // Get the last 'k' elements
let newSuffix = nums.prefix(n - k) // Get all elements excluding the last 'k'

// Concatenate newPrefix and newSuffix to form the rotated array
return Array(newPrefix + newSuffix)
// The Array() initializer is used to convert the result of the concatenation back into an Array type
}

// Example usage:
let myArray = [1, 2, 3, 4, 5]
let rotatedArray = rotateArray(myArray, 2) // rotatedArray is [4, 5, 1, 2, 3]

print(rotatedArray)

Method 3: Using dropFirst() and dropLast()

// Swift function to rotate an array to the right by k steps using dropFirst and dropLast
func rotateArray(_ nums: [Int], _ k: Int) -> [Int] {
let n = nums.count // Get the count of elements in the array
guard n > 0, k > 0 else { return nums } // If array is empty or k is 0, no rotation is needed
let k = k % n // Use modulus in case k is larger than array size to get the effective rotation step

// Use dropLast to get the left part of the array and dropFirst to get the right part
let leftPart = Array(nums.dropLast(k)) // Drop the last 'k' elements to get the left part
let rightPart = Array(nums.dropFirst(n - k)) // Drop the left 'n-k' elements to get the right part

// Concatenate rightPart and leftPart to form the rotated array
return rightPart + leftPart
}

// Example usage:
let myArray = [1, 2, 3, 4, 5]
let rotatedArray = rotateArray(myArray, 2) // rotatedArray is [4, 5, 1, 2, 3]

print(rotatedArray)

Method 4: In-place Rotation with Reversal

// Swift function to rotate an array to the right by k steps
func rotateArray(_ nums: inout [Int], _ k: Int) {
let n = nums.count // Get the count of elements in the array
let k = k % n // Use modulus in case k is larger than array size to get the effective rotation

reverse(&nums, 0, n - 1) /// Reverse the whole array

reverse(&nums, 0, k - 1) /// Reverse the first k elements to put them at the beginning

reverse(&nums, k, n - 1) /// Reverse the remaining elements to maintain the original order
}

// Helper function to reverse elements in the array between indices start and end
func reverse(_ nums: inout [Int], _ start: Int, _ end: Int) {
var start = start // Starting index
var end = end // Ending index

// Continue swapping elements until the start index is less than the end index
while start < end {
nums.swapAt(start, end) // Swap elements at start and end indices
start += 1 // Move start index to the right
end -= 1 // Move end index to the left
}
}

// Example usage:
var myArray = [1, 2, 3, 4, 5] // Original array
rotateArray(&myArray, 2) // Rotate array by 2 steps to the right, myArray becomes [4, 5, 1, 2, 3]

print(myArray)

Method 5: Using the Array's rotate method (Swift 5.5 and later)

// Swift function to rotate an array to the right by k steps using higher order functions
func rotateArray(_ nums: [Int], _ k: Int) -> [Int] {
let n = nums.count // Get the count of elements in the array
guard n > 0, k > 0 else { return nums } // If array is empty or k is 0, no rotation is needed
let k = k % n // Use modulus in case k is larger than array size to get the effective rotation step

// Concatenate two slices of the array to achieve the rotation:
// 1. The last 'k' elements (from index 'n-k' to 'n-1')
// 2. The first 'n-k' elements (from index '0' to 'n-k-1')
return Array(nums[(n - k)..<n] + nums[0..<(n - k)])
// The Array() initializer is used to convert the result of the concatenation back into an Array type
}

// Example usage:
let myArray = [1, 2, 3, 4, 5]
let rotatedArray = rotateArray(myArray, 2) // rotatedArray is [4, 5, 1, 2, 3]

or we can use the following approach:

// Extend the Array type to include a rotate function
extension Array {
// Rotate the array to the right by 'k' steps
mutating func rotate(rightBy k: Int) {
let k = k % self.count // Ensure k is within the bounds of the array length
if k != 0 {
// Take the last 'k' elements, creating a new array from that slice
let leftPart = Array(self[self.count-k..<self.count])
// Take the elements up to 'self.count - k' to form the right part
let rightPart = Array(self[0..<self.count-k])
// Concatenate the left and right parts to complete the rotation
self = leftPart + rightPart
}
}
}

// Function to rotate an array by 'k' steps to the right
func rotateArray(_ nums: [Int], _ k: Int) -> [Int] {
var nums = nums // Copy of the input array to mutate
nums.rotate(rightBy: k) // Rotate the array using the rotate function
return nums // Return the rotated array
}

// Example usage:
let myArray = [1, 2, 3, 4, 5] // Original array
let rotatedArray = rotateArray(myArray, 2) // Rotate the array by 2 steps to the right
// rotatedArray is now [4, 5, 1, 2, 3]

print(rotatedArray)

Method 6: Using Array's append(contentsOf:) and removeFirst(_:)

func rotateArray(_ nums: [Int], _ k: Int) -> [Int] {
var nums = nums
let n = nums.count
let k = k % n // Ensure k is within the bounds of the array length
let lastPart = Array(nums[(n-k)...]) // Convert the last k elements to an Array
nums.removeLast(k) // Remove the last k elements
nums.insert(contentsOf: lastPart, at: 0) // Insert the last k elements at the beginning
return nums
}

// Example usage:
let myArray = [1, 2, 3, 4, 5] // Original array
let rotatedArray = rotateArray(myArray, 2) // Rotate the array by 2 steps to the right
// rotatedArray is now [4, 5, 1, 2, 3]

print(rotatedArray)

Generalization

Method 1: Slicing and Concatenation

func rotateArray<T>(_ nums: [T], _ k: Int) -> [T] {
let n = nums.count
guard n > 0, k > 0 else { return nums }
let k = k % n // Use modulus to get the effective rotation steps within array bounds

// Perform slicing using the correct indices and concatenation using generic type T
let leftPart = nums[(n - k)..<n] // Slice from 'n-k' to the end of the array
let rightPart = nums[0..<n-k] // Slice from start of the array to 'n-k'

// Concatenate the two slices to produce the rotated array
return Array(leftPart) + Array(rightPart)
}

// Example usage with Int array:
let intArray = [1, 2, 3, 4, 5]
let rotatedIntArray = rotateArray(intArray, 2) // rotatedIntArray is [4, 5, 1, 2, 3]

// Example usage with String array:
let stringArray = ["a", "b", "c", "d", "e"]
let rotatedStringArray = rotateArray(stringArray, 2) // rotatedStringArray is ["d", "e", "a", "b", "c"]

print(rotatedIntArray)
print(rotatedStringArray)

The rotateArray function, as defined with a generic type T, can accept any data type that conforms to the Collection protocol because it relies on operations like count, slicing, and concatenation that are defined for collections in Swift. This includes but is not limited to:

  • Arrays of any type, e.g., [Int], [String], [Double], [CustomType], etc.
  • ArraySlices of any type, e.g., ArraySlice<Int>, ArraySlice<String>, etc.
  • Other collection types that can be converted to arrays, such as Set or Dictionary.Values (though these would lose their set or dictionary characteristics when converted to arrays).

Here’s a more detailed breakdown:

  • Primitive Data Types: Int, Float, Double, Bool, Character, String, etc.
  • Structures: Any struct that you define, given that it can be contained in an array.
  • Classes: Any class instances, as long as they can be contained in an array.
  • Enums: Enumeration cases, particularly if they have raw values or are CaseIterable.
  • Tuples: Although Swift does not support arrays of tuples with different types, you can have an array of tuples as long as all tuples in the array are of the same type.

The key requirement is that the elements can be put into an array and that the array can be sliced and concatenated, which is true for most types in Swift. However, the behavior of the rotation might only make sense for certain types of data (e.g., rotating an array of UIView might not be meaningful).

Let's explore a sample with CustomType:

// Define a custom struct to represent a Person with a name and age
struct Person {
var name: String
var age: Int
}

// The generic rotateArray function can work with any type, including custom types like Person
func rotateArray<T>(_ nums: [T], _ k: Int) -> [T] {
let n = nums.count // Determine the number of elements in the array
guard n > 0, k > 0 else { return nums } // If array is empty or k is 0, no rotation is needed
let k = k % n // Use modulus to get the effective rotation steps within array bounds

// Split the array into two parts based on the rotation point and concatenate them
let leftPart = nums[(n - k)..<n] // Take the last 'k' elements
let rightPart = nums[0..<n-k] // Take the first 'n-k' elements

// Concatenate the two parts to get the rotated array and return it
return Array(leftPart) + Array(rightPart)
}

// Example usage with an array of Person objects
let peopleArray = [
Person(name: "Alice", age: 30),
Person(name: "Bob", age: 25),
Person(name: "Charlie", age: 35),
Person(name: "Diana", age: 42),
Person(name: "Ethan", age: 28)
]

// Rotate the array of Person objects by 2 positions
let rotatedPeopleArray = rotateArray(peopleArray, 2)

// Print the names of the original array for comparison purposes
print("Original array:")
for person in peopleArray {
print(person.name)
}

// Print a separator for clarity in output
print("\nRotated array:")
// Print the names of the rotated array to show the effect of the rotation
for person in rotatedPeopleArray {
print(person.name)
}

Practical use cases of rotating array in iOS development

Here is an exhaustive list of practical use cases for rotating arrays in iOS development:

  1. User Interface Carousels: Rotating images, views, or elements in a carousel or a paged scroll view.
  2. Circular Menus: Implementing circular or radial menus where selection rotates the menu items.
  3. Infinite Lists: Creating the illusion of an infinite scrolling list by rotating data elements.
  4. Games: Cycling through available weapons, tools, or inventory items.
  5. Content Feeds: Rotating through news articles, tutorial steps, or educational content in an app.
  6. Ad Banners: Rotating promotional banners or advertisements in an app.
  7. Data Visualization: Rotating through sets of data points or graphs, especially in dashboard apps.
  8. Calendar and Event Apps: Showing upcoming events or dates in a rotating fashion.
  9. Music and Video Apps: Browsing through albums, playlists, or channels by rotating through the list.
  10. Tutorial Walkthroughs: Cycling through tutorial screens or onboarding information.
  11. Weather Widgets: Rotating through hourly or daily weather forecasts.
  12. Sports Apps: Rotating through scores, stats, or player profiles.
  13. E-commerce: Displaying products, offers, or categories in a rotating banner.
  14. Chatbots and AI Assistants: Rotating through suggested responses or prompts.
  15. Health and Fitness Apps: Rotating through workout routines or health tips.
  16. Educational Apps: Flashcards or quiz apps where questions and answers rotate.
  17. Navigation and Maps: Rotating through different layers or types of information on a map.
  18. Social Media: Rotating through stories or status updates from friends or followed accounts.
  19. Stock and Finance Apps: Rotating through stock tickers or financial indicators.
  20. Recipe Apps: Browsing through recipes or cooking steps.

Rotating arrays is a common operation when the app needs to continuously cycle through a set of data, presenting it to the user dynamically and engagingly, or when the data needs to be reordered without adding or removing elements.

Here’s a simplified example of using array rotation for an image gallery:

import UIKit

class ImageGalleryViewController: UIViewController {
var images: [UIImage] = [...] // Array of images
var currentIndex: Int = 0 // Current index in the gallery

func rotateImages(toRight: Bool) {
// Calculate the rotation based on the direction
let rotationSteps = toRight ? 1 : images.count - 1
images = rotateArray(images, rotationSteps)

// Update the UI with the new images
updateImageViewsWithRotatedImages()
}

func updateImageViewsWithRotatedImages() {
// Code to update the image views with the rotated images array
// For example, imageView.image = images[currentIndex]
}
}

// Usage:
let galleryVC = ImageGalleryViewController()
galleryVC.rotateImages(toRight: true) // Rotates images to the right

In this scenario, rotateImages(toRight:) is called to rotate the images when the user interacts with the gallery (e.g., swipes left or right). The gallery view would then be updated with the new set of images. This is a common pattern in carousel views or when implementing custom paging behavior.

If you reach this point and are still curious about the full implementation of `ImageGalleryViewController`, I got you!
Follow this link for more: https://medium.com/@CongLeSolutionX/a-full-implementation-of-array-rotation-in-ios-app-afa0fbdf9229

Stackademic 🎓

Thank you for reading until the end. Before you go:

--

--