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 feedback@compilenrun.com. I’d greatly appreciate your feedback! :)