Swift
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Optionals
-
Section 4: Object-Oriented and Value Types
-
Section 5: Memory Management
-
Section 6: Generics and Error Handling
-
Section 7: Concurrency
-
Section 8: Working with Collections
-
Section 9: Codable and Data Handling
-
Section 10: Protocol-Oriented Programming
-
Section 11: Testing and Tooling
-
Section 12: Practical Projects
-
Section 13: Interview Practice
-
Section 14: More Practice Exercises
-
Section 15: More Standard Library
-
Section 16: Advanced Concurrency
-
Section 17: Foundation Framework Deep Dive
-
Section 18: URLSession and Networking Deep Dive
-
Section 19: Combine Framework
-
Section 20: SwiftUI Fundamentals for Swift Developers
-
Section 21: Server-Side Swift with Vapor
-
Section 22: Swift Package Manager Deep Dive
-
Section 23: Swift Concurrency Deep Dive
-
Section 24: More Language Features
-
Section 25: Error Handling Deep Dive
-
Section 26: Testing Deep Dive
-
Section 27: Data Structures and Algorithms in Swift
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Swift Macros (Swift 5.9+)
-
Section 31: Property Wrappers Ecosystem
-
Section 32: Swift Interop Deep Dive
-
Section 33: iOS App Architecture Patterns
-
Section 34: Performance and Debugging
-
Section 35: App Distribution and CI/CD
-
Section 36: More Practical Projects
-
Section 37: SwiftData and Persistence
-
Section 38: More Design Patterns
-
Section 39: More Review and Practice
-
Section 40: More Foundation Deep Dive
-
Section 41: Advanced Collections in Swift
-
Section 42: Advanced Generics Practice
-
Section 43: UIKit for Legacy and Hybrid Apps
-
Section 44: watchOS and visionOS Development Basics
-
Section 45: More Networking Patterns
-
Section 46: More Testing Practice
-
Section 47: Accessibility in Swift Apps
-
Section 48: Localization
-
Section 49: More Practical Projects Round 2
-
Section 50: Swift Charts Framework
-
Section 51: More Interview and Algorithm Practice
-
Section 52: Final Practice and Mastery
-
Section 53: Swift Compiler and Build System
-
Section 54: More Concurrency Practice
-
Section 55: App Store Guidelines and Review
-
Section 56: More Design and Architecture
36: Higher-Order Functions: map, filter, reduce
I've spent a lot of time reviewing PRs from developers moving into Swift, and there is one mistake that pops up constantly when they hit higher-order functions. They treat map as if it's just a shorthand for a for-in loop.
Stop using map to "do things"
Here is the mistake: I'll see someone write code like this to log a list of user names:
let users = ["Alice", "Bob", "Charlie"]
users.map { user in
print("Processing user: \(user)")
}
Technically, this "works"—the names get printed. But logically, it's a disaster. Why? Because map is designed to transform data and return a new array. When you use map just to trigger a side effect (like printing), you're actually creating a brand new array of Void values in memory that you immediately throw away. It's wasteful and confusing to anyone reading your code.
If you just want to iterate and perform an action, use forEach or a standard for loop. Use map only when you want to turn "Array A" into "Array B".
Transforming data with map
Think of map as a conveyor belt. An item comes in, you change it into something else, and it moves into a new collection. Let's say we have a list of Product objects, and we only care about their prices for a calculation.
struct Product {
let name: String
let price: Double
}
let cart = [
Product(name: "Mechanical Keyboard", price: 120.0),
Product(name: "USB-C Cable", price: 15.0),
Product(name: "Gaming Mouse", price: 80.0)
]
// We transform an array of Products into an array of Doubles
let prices = cart.map { $0.price }
// Result: [120.0, 15.0, 80.0]
Notice how the type changed. We started with [Product] and ended with [Double]. That's the essence of mapping.
Sifting through noise with filter
While map changes the type or value of the elements, filter changes the number of elements. It takes a closure that returns a Boolean; if the closure returns true, the element stays. If false, it's tossed out.
If we only want to see the "premium" items in our cart (those over $50), we do this:
let premiumProducts = cart.filter { $0.price > 50.0 }
// Result: [Product(name: "Mechanical Keyboard", ...), Product(name: "Gaming Mouse", ...)]
The type remains [Product], but the collection is smaller. I love using this to clean up data before it hits the UI, so the user doesn't see irrelevant entries.
Collapsing a collection with reduce
Now, reduce is the one that usually makes people's heads spin. I find it easiest to think of it as "the accumulator." Instead of returning a new array, reduce boils the entire collection down to a single value.
It requires two things: an initial starting value and a closure that defines how to combine the current running total with the next element in the list.
let totalCost = cart.reduce(0) { accumulator, product in
accumulator + product.price
}
// Result: 215.0
In the first iteration, the accumulator is 0. It adds 120.0. In the second iteration, the accumulator is 120.0, and it adds 15.0, and so on. Swift allows us to write this even more concisely using the + operator directly if we've already mapped the prices:
let quickTotal = prices.reduce(0, +)
When you chain these together—filter, then map, then reduce—you can perform complex data processing in a few readable lines without ever having to manage a mutable var array or a manual loop counter.
📋 Practical Task
Exercise: Calculating the Final Invoice Total
You are building a checkout system. You have an array of OrderItem objects. Some items are marked as "discounted," and some are "tax-exempt."
Your Task: Write a pipeline using filter, map, and reduce to calculate the final total price of the order based on these rules:
- First, filter out any items that have a quantity of 0 (they shouldn't be in the total).
- Then, map the items to their total cost (price * quantity).
- Finally, reduce those costs into a single sum.
struct OrderItem {
let name: String
let price: Double
let quantity: Int
}
let order = [
OrderItem(name: "Laptop", price: 1200.0, quantity: 1),
OrderItem(name: "Mouse Pad", price: 15.0, quantity: 0),
OrderItem(name: "HDMI Cable", price: 20.0, quantity: 2),
OrderItem(name: "Webcam", price: 60.0, quantity: 0),
OrderItem(name: "USB Hub", price: 40.0, quantity: 1)
]
// Your code here:
let finalTotal = // ... chain your functions here
print("The final invoice total is: $\(finalTotal)")
// Expected output: The final invoice total is: $1280.0
There are no comments for now.