Skip to main content

Swift Collection Iteration

Collections are one of the fundamental building blocks in programming, and the ability to efficiently process their elements is a crucial skill. Swift provides several elegant ways to iterate through collections, from traditional loops to more concise functional approaches.

Introduction to Collection Iteration

Iteration is the process of accessing each element in a collection sequentially. In Swift, you can iterate through arrays, dictionaries, sets, and other collection types. Understanding different iteration techniques helps you write cleaner, more efficient code and solve problems more effectively.

Basic Iteration with For-In Loops

The most straightforward way to iterate through a Swift collection is using a for-in loop.

Iterating Through Arrays

swift
let fruits = ["Apple", "Banana", "Cherry", "Date"]

// Basic for-in loop
for fruit in fruits {
print(fruit)
}

/* Output:
Apple
Banana
Cherry
Date
*/

Accessing Index and Value Together

Sometimes you need both the index and value during iteration. Swift's enumerated() method provides this functionality:

swift
for (index, fruit) in fruits.enumerated() {
print("\(index + 1). \(fruit)")
}

/* Output:
1. Apple
2. Banana
3. Cherry
4. Date
*/

Iterating Through Dictionaries

Dictionaries store key-value pairs, and Swift makes it easy to access both:

swift
let fruitColors = ["Apple": "Red", "Banana": "Yellow", "Cherry": "Red"]

// Accessing key and value
for (fruit, color) in fruitColors {
print("\(fruit) is \(color)")
}

/* Output:
Cherry is Red
Banana is Yellow
Apple is Red
*/

// Accessing just keys
for fruit in fruitColors.keys {
print(fruit)
}

// Accessing just values
for color in fruitColors.values {
print(color)
}

Iterating Through Sets

Sets work just like arrays in basic iteration:

swift
let primaryColors: Set = ["Red", "Blue", "Yellow"]

for color in primaryColors {
print(color)
}

/* Output (order may vary since sets are unordered):
Red
Blue
Yellow
*/

Range-Based Iteration

You can iterate through numeric ranges using Swift's range operators:

swift
// Closed range (includes both endpoints)
for number in 1...5 {
print(number)
}
/* Output:
1
2
3
4
5
*/

// Half-open range (excludes upper bound)
for number in 1..<5 {
print(number)
}
/* Output:
1
2
3
4
*/

// With stride to skip items
for number in stride(from: 1, through: 10, by: 2) {
print(number)
}
/* Output:
1
3
5
7
9
*/

Functional Iteration Methods

Swift provides several higher-order functions for working with collections that can make your code more concise and expressive.

forEach

Similar to a for loop but in a method form:

swift
fruits.forEach { fruit in
print("I like \(fruit)")
}

// Shorter syntax
fruits.forEach { print("I like \($0)") }

map

Transforms each element of a collection:

swift
let uppercaseFruits = fruits.map { $0.uppercased() }
print(uppercaseFruits)
// Output: ["APPLE", "BANANA", "CHERRY", "DATE"]

let fruitLengths = fruits.map { fruit in
return fruit.count
}
print(fruitLengths)
// Output: [5, 6, 6, 4]

filter

Creates a new collection containing only elements that satisfy a condition:

swift
let longFruits = fruits.filter { $0.count > 5 }
print(longFruits)
// Output: ["Banana", "Cherry"]

reduce

Combines all elements into a single value:

swift
let totalCharacters = fruits.reduce(0) { total, fruit in
return total + fruit.count
}
print("Total characters in all fruits: \(totalCharacters)")
// Output: Total characters in all fruits: 21

let fruitSalad = fruits.reduce("") { $0 + $1 + " " }
print(fruitSalad)
// Output: "Apple Banana Cherry Date "

compactMap and flatMap

compactMap works like map but removes nil values and unwraps optionals:

swift
let stringNumbers = ["1", "2", "three", "4", "five"]
let numbers = stringNumbers.compactMap { Int($0) }
print(numbers)
// Output: [1, 2, 4]

flatMap transforms and flattens nested collections:

swift
let nestedNumbers = [[1, 2], [3, 4, 5], [6]]
let flattened = nestedNumbers.flatMap { $0 }
print(flattened)
// Output: [1, 2, 3, 4, 5, 6]

Practical Examples

Building a Shopping Cart Total

swift
struct Product {
let name: String
let price: Double
}

let cart = [
Product(name: "Laptop", price: 1299.99),
Product(name: "Mouse", price: 24.99),
Product(name: "Keyboard", price: 49.99),
Product(name: "Monitor", price: 199.99)
]

let total = cart.reduce(0) { $0 + $1.price }
print("Cart total: $\(total)")
// Output: Cart total: $1574.96

// With tax calculation
let taxRate = 0.08
let totalWithTax = cart.reduce(0) { $0 + $1.price } * (1 + taxRate)
print("Cart total with tax: $\(totalWithTax)")
// Output: Cart total with tax: $1700.9568

Building a Search Filter

swift
struct Book {
let title: String
let author: String
let genre: String
let rating: Double
}

let books = [
Book(title: "The Swift Programming Language", author: "Apple Inc.", genre: "Programming", rating: 4.5),
Book(title: "Clean Code", author: "Robert C. Martin", genre: "Programming", rating: 4.8),
Book(title: "1984", author: "George Orwell", genre: "Fiction", rating: 4.7),
Book(title: "To Kill a Mockingbird", author: "Harper Lee", genre: "Fiction", rating: 4.6)
]

// Find programming books with rating ≥ 4.7
let recommendedProgrammingBooks = books.filter { $0.genre == "Programming" && $0.rating >= 4.7 }

recommendedProgrammingBooks.forEach { book in
print("\(book.title) by \(book.author) - Rating: \(book.rating)")
}

/* Output:
Clean Code by Robert C. Martin - Rating: 4.8
*/

Processing Data from an API

Let's simulate processing data from an API response:

swift
// Simulated JSON response data
let jsonData = """
[
{"name": "Alice", "age": 24, "active": true},
{"name": "Bob", "age": null, "active": true},
{"name": "Charlie", "age": 31, "active": false},
{"name": "Diana", "age": 28, "active": true}
]
"""

struct User {
let name: String
let age: Int
let active: Bool
}

// Simulated parsing (in reality you would use JSONDecoder)
let parsedUsers = [
["name": "Alice", "age": 24, "active": true],
["name": "Bob", "age": nil, "active": true],
["name": "Charlie", "age": 31, "active": false],
["name": "Diana", "age": 28, "active": true]
]

// Creating user objects, handling potential missing data
let users = parsedUsers.compactMap { userData -> User? in
guard
let name = userData["name"] as? String,
let active = userData["active"] as? Bool,
let age = userData["age"] as? Int
else {
return nil
}

return User(name: name, age: age, active: active)
}

// Get active users and their average age
let activeUsers = users.filter { $0.active }
let averageAge = activeUsers.reduce(0) { $0 + $1.age } / activeUsers.count

print("Active users: \(activeUsers.count)")
print("Average age of active users: \(averageAge)")

/* Output:
Active users: 2
Average age of active users: 26
*/

When to Use Each Iteration Method

  • For-in loops: Great for general purposes and when you need to use control flow statements like break and continue
  • forEach: Cleaner alternative to for-in when you don't need control flow statements
  • map: When you need to transform each element in a collection
  • filter: When you need to extract elements that match a condition
  • reduce: When you need to combine all elements into a single value
  • compactMap: When dealing with collections of optionals or transformations that might produce optionals
  • flatMap: When working with nested collections that you want to flatten

Performance Considerations

For most applications, the choice between iteration methods has minimal performance impact. However, in performance-critical code:

  • Standard for-in loops can be slightly faster for simple operations
  • Functional methods like map and filter create new collections, which uses additional memory
  • Consider using lazy collections for potentially better performance with large collections:
swift
let numbers = Array(1...1000000)
let evenSquares = numbers.lazy.filter { $0 % 2 == 0 }.map { $0 * $0 }
print(evenSquares[4]) // Only computes what's needed

Summary

Swift offers a rich set of tools for collection iteration, from traditional loops to powerful functional methods. The approach you choose depends on your specific needs and coding style preferences:

  • For-in loops provide control flow flexibility
  • Functional methods like map, filter, and reduce offer concise, expressive syntax
  • Specialized iteration methods like enumerated(), zip(), and stride() solve specific iteration problems

Mastering these iteration techniques will help you write cleaner, more efficient Swift code and handle data processing tasks with greater ease.

Exercises

  1. Create an array of integers and use filter to extract only the even numbers, then use map to square each of those numbers.
  2. Create a dictionary of student names and scores, then use iteration to find the average score and identify students with scores above the average.
  3. Create an array of strings and use reduce to concatenate them into a single string, separated by commas.
  4. Use enumerated() and a for-in loop to print out each element of an array prefixed with its position (1-indexed).
  5. Create a nested array structure and use flatMap to flatten it, then filter the resulting array for values greater than 10.

Additional Resources

Happy coding with Swift collections!



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)