Swift Collection Transformations
Collection transformations are powerful operations that allow you to process and modify the contents of collections in Swift. Rather than writing verbose loops to iterate through elements, transformations offer a concise, expressive way to manipulate data. In this lesson, we'll explore the most common collection transformation methods in Swift.
Introduction to Collection Transformations
Swift collections (Arrays, Sets, Dictionaries) include several built-in methods that follow functional programming principles. These methods allow you to:
- Transform each element into something new (
map) - Filter elements based on conditions (
filter) - Combine elements into a single result (
reduce) - And more!
These operations make your code more readable, maintainable, and often more efficient. Let's dive into each one with examples.
The map Method
The map method transforms each element in a collection using a closure and returns a new array containing the transformed elements.
Basic Syntax
let transformedCollection = collection.map { element in
// Return transformed element
}
Example: Converting Numbers to Strings
let numbers = [1, 2, 3, 4, 5]
let stringNumbers = numbers.map { String($0) }
print(stringNumbers) // Output: ["1", "2", "3", "4", "5"]
Here, map transforms each integer into its string representation.
Example: Calculating Prices with Tax
let prices = [10.0, 20.0, 30.0, 40.0]
let taxRate = 0.1
let pricesWithTax = prices.map { price in
return price * (1 + taxRate)
}
print(pricesWithTax) // Output: [11.0, 22.0, 33.0, 44.0]
The filter Method
The filter method creates a new collection containing only the elements that satisfy a provided condition.
Basic Syntax
let filteredCollection = collection.filter { element in
// Return true to include element, false to exclude
}
Example: Finding Even Numbers
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let evenNumbers = numbers.filter { $0 % 2 == 0 }
print(evenNumbers) // Output: [2, 4, 6, 8, 10]
Example: Filtering Products by Price
struct Product {
let name: String
let price: Double
}
let inventory = [
Product(name: "Apple", price: 0.99),
Product(name: "Banana", price: 0.59),
Product(name: "Mango", price: 2.99),
Product(name: "Pineapple", price: 3.99)
]
let affordableProducts = inventory.filter { $0.price < 1.00 }
// Output: Products with name "Apple" and "Banana"
The reduce Method
The reduce method combines all elements in a collection into a single value.
Basic Syntax
let result = collection.reduce(initialValue) { partialResult, element in
// Combine partialResult and element to form a new partialResult
}
Example: Summing Numbers
let numbers = [1, 2, 3, 4, 5]
let sum = numbers.reduce(0) { $0 + $1 }
// Alternatively: let sum = numbers.reduce(0, +)
print(sum) // Output: 15
Example: Creating a Sentence from Words
let words = ["Swift", "is", "a", "powerful", "language"]
let sentence = words.reduce("") { $0 + ($0.isEmpty ? "" : " ") + $1 }
print(sentence) // Output: "Swift is a powerful language"
Chaining Transformations
One of the most powerful aspects of collection transformations is that they can be chained together to perform complex operations succinctly.
Example: Processing a List of Numbers
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let sumOfSquaresOfEvenNumbers = numbers
.filter { $0 % 2 == 0 } // [2, 4, 6, 8, 10]
.map { $0 * $0 } // [4, 16, 36, 64, 100]
.reduce(0, +) // 220
print(sumOfSquaresOfEvenNumbers) // Output: 220
This chain:
- Filters to keep only even numbers
- Maps each number to its square
- Reduces the results to a sum
Additional Collection Transformations
The flatMap Method
flatMap flattens a collection of collections into a single collection and transforms elements.
let nestedArrays = [[1, 2], [3, 4], [5, 6]]
let flattened = nestedArrays.flatMap { $0 }
print(flattened) // Output: [1, 2, 3, 4, 5, 6]
The compactMap Method
compactMap transforms elements like map, but also removes any nil results.
let possibleNumbers = ["1", "2", "three", "4", "five"]
let validNumbers = possibleNumbers.compactMap { Int($0) }
print(validNumbers) // Output: [1, 2, 4]
The forEach Method
forEach performs an operation on each element but doesn't return anything.
let numbers = [1, 2, 3, 4, 5]
numbers.forEach { number in
print("Number: \(number)")
}
// Output:
// Number: 1
// Number: 2
// Number: 3
// Number: 4
// Number: 5
Real-World Applications
Example: Processing User Data
Consider an application that needs to process user data:
struct User {
let id: Int
let name: String
let age: Int
let isActive: Bool
}
let users = [
User(id: 1, name: "Alice", age: 28, isActive: true),
User(id: 2, name: "Bob", age: 19, isActive: false),
User(id: 3, name: "Charlie", age: 35, isActive: true),
User(id: 4, name: "Diana", age: 17, isActive: true),
User(id: 5, name: "Edward", age: 42, isActive: false)
]
// Get names of active adults (18+)
let activeAdultNames = users
.filter { $0.isActive && $0.age >= 18 }
.map { $0.name }
print(activeAdultNames) // Output: ["Alice", "Charlie"]
// Get average age of all users
let totalAge = users.reduce(0) { $0 + $1.age }
let averageAge = Double(totalAge) / Double(users.count)
print("Average age: \(averageAge)") // Output: Average age: 28.2
Example: Working with JSON Data
let jsonString = """
[
{"name": "Apple", "price": 0.99, "inStock": true},
{"name": "Orange", "price": 1.29, "inStock": true},
{"name": "Banana", "price": 0.59, "inStock": false},
{"name": "Mango", "price": 2.99, "inStock": true}
]
"""
// Parse JSON
if let data = jsonString.data(using: .utf8),
let jsonArray = try? JSONSerialization.jsonObject(with: data) as? [[String: Any]] {
// Get names of in-stock fruits under $1
let affordableFruits = jsonArray
.filter { ($0["inStock"] as? Bool) == true && ($0["price"] as? Double ?? 0) < 1.0 }
.compactMap { $0["name"] as? String }
print(affordableFruits) // Output: ["Apple"]
}
Performance Considerations
While collection transformations make code more readable, it's important to consider performance implications:
-
Multiple passes: Each transformation creates a new collection, which can be inefficient for large datasets.
-
Eager evaluation: Transformations are evaluated immediately. For large collections, consider using
lazyfor better performance:
let numbers = Array(1...1000000)
let result = numbers.lazy.filter { $0 % 2 == 0 }.map { $0 * 2 }.prefix(10)
print(Array(result)) // Only processes what's needed for the first 10 results
Summary
Swift's collection transformations provide a powerful, expressive way to manipulate data:
- map: Transform each element into something new
- filter: Keep only elements that meet a condition
- reduce: Combine all elements into a single result
- flatMap: Flatten nested collections while transforming
- compactMap: Transform and remove nil values
- forEach: Perform an operation on each element
These methods can be chained together to create concise, readable solutions for complex data processing tasks. By embracing these functional programming concepts, you can write more maintainable and expressive Swift code.
Additional Resources
Exercises
- Use
mapto convert an array of temperatures in Celsius to Fahrenheit. - Use
filterto find all strings in an array that have more than 5 characters. - Use
reduceto find the longest string in an array. - Chain transformations to find the sum of squares of all odd numbers in an array.
- Use
compactMapto extract valid URLs from an array of strings.
💡 Found a typo or mistake? Click "Edit this page" to suggest a correction. Your feedback is greatly appreciated!