Swift: Swift Higher-Order Functions

Higher-order functions are functions that accept other functions as arguments or return functions. The Swift standard library provides powerful higher-order functions like map, filter, and reduce, letting you process collections in a declarative style.

1. What You'll Learn


2. A Real-World Data Analyst Story

(1) Pain Point: Bloated for-Loop Code for Array Processing

Bob needs to extract active users from an array, sort them by age, and format the output strings:

SWIFT
var activeUsers: [String] = []
for user in users {
    if user.isActive {
        activeUsers.append(user.name)
    }
}
activeUsers.sort()
var result: [String] = []
for name in activeUsers {
    result.append("User: \(name)")
}

Two for-loops + a temporary array + a sort. The logic is simple but the code is long and filled with "How" details instead of expressing "What."

(2) The Higher-Order Function Solution

Use functional chaining to accomplish everything in one line:

SWIFT
let result = users
    .filter { $0.isActive }
    .map { $0.name }
    .sorted()
    .map { "User: \($0)" }

Code goes from 10 lines to 4, with better readability.

(3) Benefit: The Advantages of Declarative Programming

Dimension for Loop Higher-Order Chain
Code Length 10-15 lines 3-5 lines
Readability Read loop body to understand intent Function names directly express intent
Data Flow Track intermediate variables Chain clearly shows data flow
Change Impact Rewrite loop for logic changes Add/remove steps without affecting others
Concurrency Manual management Inherently parallel-friendly (e.g. parallelMap)

3. map: Transform Each Element

map applies a transformation to each element in an array and returns a new array of the same length.

100%
graph LR
    A["[1, 2, 3, 4, 5]"] --> B["map { $0 * 2 }"]
    B --> C["[2, 4, 6, 8, 10]"]
Scenario Input Transform Output
Double values [1, 2, 3] { $0 * 2 } [2, 4, 6]
Extract property [User, User] { $0.name } ["Alice", "Bob"]
Type conversion ["1", "2"] { Int($0) } [1, 2] (type becomes [Int?])

▶ Example: Basic map Usage

SWIFT
// ============================================
// map: Apply transformation to each element
// ============================================
let numbers = [1, 2, 3, 4, 5]
// for-loop approach (old style)
var doubled: [Int] = []
for n in numbers { doubled.append(n * 2) }
// map approach (modern style)
let doubledMap = numbers.map { $0 * 2 }
print("doubledMap: \(doubledMap)")
// Extract string lengths
let words = ["Swift", "Python", "Go"]
let lengths = words.map { $0.count }
print("lengths: \(lengths)")

Output:

TEXT 📖 Display only
doubledMap: [2, 4, 6, 8, 10]
lengths: [5, 6, 2]

4. filter: Select Elements

filter keeps elements that satisfy a condition, returning a new array.

▶ Example: filter Selection Operations

SWIFT
// ============================================
// filter: Keep elements that satisfy a condition
// ============================================
let scores = [45, 82, 91, 63, 77, 55, 88]
// Filter passing scores (>= 60)
let passed = scores.filter { $0 >= 60 }
print("Passed: \(passed)")
// Filter excellent scores (>= 85)
let excellent = scores.filter { $0 >= 85 }
print("Excellent: \(excellent)")
// Chaining: filter failing scores then double them
let failedDoubled = scores
    .filter { $0 < 60 }
    .map { $0 * 2 }
print("Failed scores doubled: \(failedDoubled)")

Output:

TEXT 📖 Display only
Passed: [82, 91, 63, 77, 88]
Excellent: [91, 88]
Failed scores doubled: [90, 110]

Tip: filter returns a new array preserving the original order.


5. reduce and compactMap

(1) reduce: Combine into a Single Value

reduce combines all elements of an array into a single value.

Parameter Meaning Example
initialResult Starting value 0 for summing, "" for concatenation
nextPartialResult Accumulating closure { $0 + $1 }
100%
graph LR
    A["[1, 2, 3, 4]"] --> B["reduce(0) { $0 + $1 }"]
    B --> C["0+1=1, 1+2=3, 3+3=6, 6+4=10"]
    C --> D["10"]

▶ Example: Various reduce Use Cases

SWIFT
// ============================================
// reduce: Reduction operations
// ============================================
let numbers = [1, 2, 3, 4, 5]
// Sum
let sum = numbers.reduce(0) { $0 + $1 }
print("sum: \(sum)")
// Product
let product = numbers.reduce(1) { $0 * $1 }
print("product: \(product)")
// Concatenate strings
let words = ["Swift", "is", "awesome"]
let sentence = words.reduce("") { $0.isEmpty ? $1 : "\($0) \($1)" }
print("sentence: \(sentence)")
// Named parameters for clarity
let sumNamed = numbers.reduce(0) { total, next in total + next }
print("sumNamed: \(sumNamed)")

Output:

TEXT 📖 Display only
sum: 15
product: 120
sentence: Swift is awesome
sumNamed: 15

(2) compactMap: Filter nil Values

compactMap works like map but automatically filters out nil values.

SWIFT
// ============================================
// compactMap and flatMap
// ============================================
// compactMap: Filter nil
let strings = ["1", "two", "3", "four", "5"]
let numbers2 = strings.compactMap { Int($0) }
print("numbers: \(numbers2)")
// flatMap: Flatten nested arrays
let nested = [[1, 2], [3, 4, 5], [6]]
let flattened = nested.flatMap { $0 }
print("flattened: \(flattened)")

Output:

TEXT 📖 Display only
numbers: [1, 3, 5]
flattened: [1, 2, 3, 4, 5, 6]

6. Chaining in Practice

The most powerful use of higher-order functions is chaining — connecting multiple operations into a pipeline.

▶ Example: Complex Data Transformation Chain

SWIFT
// ============================================
// Chaining: User data analysis pipeline
// ============================================
struct User {
    let name: String
    let age: Int
    let isActive: Bool
    let scores: [Int]
}
let users = [
    User(name: "Alice", age: 28, isActive: true, scores: [85, 90, 78]),
    User(name: "Bob", age: 22, isActive: false, scores: [70, 75]),
    User(name: "Charlie", age: 35, isActive: true, scores: [95, 92, 98, 100]),
    User(name: "Diana", age: 17, isActive: true, scores: [60, 65]),
    User(name: "Eve", age: 30, isActive: false, scores: [88, 82])
]
// Chain pipeline: active users → adults → average score → sort by score → format
let report = users
    .filter { $0.isActive }
    .filter { $0.age >= 18 }
    .map { user -> (name: String, avgScore: Double) in
        let avg = Double(user.scores.reduce(0, +)) / Double(user.scores.count)
        return (user.name, avg)
    }
    .sorted { $0.avgScore > $1.avgScore }
    .map { "\($0.name): \(String(format: "%.1f", $0.avgScore))" }
print("=== Active Adult User Report ===")
report.forEach { print($0) }

Output:

TEXT 📖 Display only
=== Active Adult User Report ===
Charlie: 96.2
Alice: 84.3

7. Complete Example: Shopping Cart Price Calculator

SWIFT
// ============================================
// Complete example: Shopping cart price calculator
// Features: map + filter + reduce + compactMap combined
// ============================================
import Foundation
struct CartItem {
    let name: String
    let price: Double
    let quantity: Int
    let isDiscounted: Bool
}
let cart = [
    CartItem(name: "Laptop", price: 1299, quantity: 1, isDiscounted: false),
    CartItem(name: "Mouse", price: 29.99, quantity: 2, isDiscounted: true),
    CartItem(name: "Keyboard", price: 99.99, quantity: 1, isDiscounted: true),
    CartItem(name: "Cable", price: 9.99, quantity: 3, isDiscounted: false),
    CartItem(name: "Monitor", price: 399, quantity: 0, isDiscounted: false)
]
// 1. Filter out zero-quantity items
let validItems = cart.filter { $0.quantity > 0 }
// 2. Calculate total per item (15% off for discounted items)
let itemTotals = validItems.map { item -> (String, Double) in
    let unitPrice = item.isDiscounted ? item.price * 0.85 : item.price
    let total = unitPrice * Double(item.quantity)
    return (item.name, total)
}
// 3. Calculate grand total
let grandTotal = itemTotals.reduce(0) { $0 + $1.1 }
// 4. Format output
print("=== Shopping Cart ===")
itemTotals.forEach { print("  \($0): $\(String(format: "%.2f", $1))") }
print("  ----------")
print("  Total: $\(String(format: "%.2f", grandTotal))")

Output:

TEXT 📖 Display only
=== Shopping Cart ===
 Laptop: $1299.00
 Mouse: $50.98
 Keyboard: $84.99
 Cable: $29.97
  ----------
  Total: $1464.94

❓ FAQ

Q What's the difference between map and forEach?
A map returns a new array; forEach only iterates without returning. Use map when you want to transform data into a new array. Use forEach only for side effects (like printing).
Q What's the difference between flatMap and compactMap?
A In Swift 4.1+, flatMap flattens nested collections (e.g. [[1,2], [3]][1,2,3]), while compactMap filters out nil values (e.g. [1, nil, 3][1, 3]). Each has its own role.
Q Does chaining hurt performance?
A Each call creates a new array, so there are intermediate allocation costs. For small to medium data (a few thousand elements or fewer), the impact is negligible. For large datasets, consider using lazy for deferred evaluation: array.lazy.filter { ... }.map { ... }.
Q Can reduce handle empty arrays?
A Yes. reduce simply returns the initial value. For example, [].reduce(0, +) returns 0. However, if the initial value isn't appropriate for empty arrays (e.g. finding a maximum), check isEmpty first.
Q Can higher-order functions replace all for loops?
A Most of the time, yes. But for loops are better for certain cases: when you need to break in the middle, need indices for complex operations, or need to access adjacent elements. Use higher-order functions for: transforming, filtering, reducing, flattening.

📖 Summary


📝 Exercises

  1. Basic: Given the integer array [10, 20, 30, 40, 50], first use map to divide each element by 2, then use filter to keep elements greater than 15.
  2. Intermediate: Given the string array ["apple", "banana", "kiwi", "strawberry", "fig"], use chaining to filter strings with length >= 5, convert to uppercase, and sort alphabetically.
  3. Challenge: Given an array of Transaction structs (properties: amount: Double, category: String, date: Date), use reduce(into:) to group and sum amounts by category. Then output the total for each category.
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏