Skip to main content

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

swift
let transformedCollection = collection.map { element in
// Return transformed element
}

Example: Converting Numbers to Strings

swift
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

swift
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

swift
let filteredCollection = collection.filter { element in
// Return true to include element, false to exclude
}

Example: Finding Even Numbers

swift
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

swift
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

swift
let result = collection.reduce(initialValue) { partialResult, element in
// Combine partialResult and element to form a new partialResult
}

Example: Summing Numbers

swift
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

swift
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

swift
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:

  1. Filters to keep only even numbers
  2. Maps each number to its square
  3. 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.

swift
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.

swift
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.

swift
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:

swift
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

swift
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:

  1. Multiple passes: Each transformation creates a new collection, which can be inefficient for large datasets.

  2. Eager evaluation: Transformations are evaluated immediately. For large collections, consider using lazy for better performance:

swift
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

  1. Use map to convert an array of temperatures in Celsius to Fahrenheit.
  2. Use filter to find all strings in an array that have more than 5 characters.
  3. Use reduce to find the longest string in an array.
  4. Chain transformations to find the sum of squares of all odd numbers in an array.
  5. Use compactMap to extract valid URLs from an array of strings.


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