Swift reduce, compactMap, and flatMap Explained Clearly

Swift collections include several powerful methods for transforming data, and three of the most useful are reduce, compactMap, and flatMap. They solve different problems: combining many values into one, transforming values while removing nil, and flattening nested collections. Understanding when to use each one helps you write shorter, clearer, and more reliable Swift code.

Quick answer: Use reduce when you want one final result from many elements, such as a total or summary. Use compactMap when you want to transform values and discard nil results. Use flatMap when you want to flatten nested collections into a single collection.

Difficulty: Beginner

Helpful to know first: You'll understand this better if you already know basic Swift arrays, closures, and optionals.

1. What Are reduce, compactMap, and flatMap?

These three methods are collection operations in the Swift standard library, but they are not interchangeable. Each one has a distinct purpose.

In practical terms, think of them like this:

2. Why reduce, compactMap, and flatMap Matter

These methods matter because they let you express common data-processing tasks in a direct and readable way. Instead of writing manual loops, temporary variables, and repeated condition checks, you can describe the transformation you want more clearly.

Use them when you are working with data pipelines such as:

Do not use them just because they look concise. If a loop is clearer for a very complex operation, a loop may be the better choice. These methods are most useful when each step has one clear purpose.

3. Basic Syntax or Core Idea

reduce syntax

reduce starts with an initial result and combines each element into that result.

let numbers = [1, 2, 3, 4]
let sum = numbers.reduce(0) { partialResult, number in
    partialResult + number
}

This starts at 0, then adds each number in the array. The final value of sum is 10.

compactMap syntax

compactMap applies a transformation and keeps only non-nil results.

let texts = ["1", "2", "hello", "4"]
let values = texts.compactMap { Int($0) }

Int($0) returns an optional integer. Valid number strings become integers, and invalid values become nil. compactMap removes the nil values, so values becomes [1, 2, 4].

flatMap syntax

flatMap is commonly used when each element produces another sequence, creating nested results that should be flattened.

let groups = [[1, 2], [3, 4], [5]]
let allNumbers = groups.flatMap { $0 }

The result is a single flat array: [1, 2, 3, 4, 5].

A common source of confusion is older Swift code that used flatMap for optionals. In modern Swift, use compactMap to remove nil values from collections.

4. Step-by-Step Examples

Example 1: Using reduce to calculate a total price

Suppose you have item prices and want one final total. This is a classic reduce task because many values become one value.

let prices = [19.99, 5.50, 12.00]
let total = prices.reduce(0.0) { runningTotal, price in
    runningTotal + price
}

print(total)

This produces one final number containing the sum of all prices.

Example 2: Using compactMap to parse valid integers

When data comes from user input, a file, or an API, some values may not convert cleanly. compactMap lets you keep only valid converted values.

let rawScores = ["100", "95", "absent", "88"]
let scores = rawScores.compactMap { Int($0) }

print(scores)

The result is [100, 95, 88]. The invalid string "absent" becomes nil and is removed.

Example 3: Using flatMap to flatten nested arrays

Sometimes your data is already grouped into subarrays, but your next step needs one flat list.

let weeklySteps = [[3000, 5000], [4500], [8000, 7000]]
let allSteps = weeklySteps.flatMap { $0 }

print(allSteps)

This returns one array with all values in order.

Example 4: Combining compactMap and reduce

These methods often work well together. Here, invalid price strings are removed first, then valid prices are summed.

let rawPrices = ["10.5", "invalid", "25.0", "4.5"]

let grandTotal = rawPrices
    .compactMap { Double($0) }
    .reduce(0.0) { sum, price in
        sum + price
    }

print(grandTotal)

This is a realistic pattern in Swift: clean the data, then aggregate it.

Example 5: map vs compactMap vs flatMap

These three are often mixed up, so it helps to see them side by side.

let values = ["1", "x", "3"]

let mapped = values.map { Int($0) }
let compacted = values.compactMap { Int($0) }

let nested = [[1, 2], [3]]
let flattened = nested.flatMap { $0 }

Here, mapped is [Optional(1), nil, Optional(3)], compacted is [1, 3], and flattened is [1, 2, 3].

5. Practical Use Cases

6. Common Mistakes

Mistake 1: Using map when compactMap is needed

Beginners often use map to convert strings into numbers and then wonder why the result contains optionals.

Problem: map preserves the number of elements, so failed conversions stay in the result as nil. This can lead to optional-handling errors later.

let texts = ["1", "hello", "3"]
let numbers = texts.map { Int($0) }

Fix: Use compactMap when you want to discard failed conversions.

let texts = ["1", "hello", "3"]
let numbers = texts.compactMap { Int($0) }

The corrected version works because compactMap removes the nil values and returns a clean [Int].

Mistake 2: Using the wrong initial value with reduce

The initial value in reduce affects both the result type and the final answer.

Problem: Starting with the wrong value can produce an incorrect result or an unexpected type, especially when building strings, arrays, or dictionaries.

let numbers = [1, 2, 3]
let sum = numbers.reduce(10) { result, number in
    result + number
}

Fix: Choose the true starting value for the operation. For addition, that is usually 0.

let numbers = [1, 2, 3]
let sum = numbers.reduce(0) { result, number in
    result + number
}

The corrected version works because the accumulator starts from the correct neutral value.

Mistake 3: Expecting flatMap to remove nil values from a collection

Older Swift examples sometimes show flatMap being used for optional removal, which confuses many learners.

Problem: In modern Swift, using flatMap for this purpose is the wrong tool and can lead to confusing type errors or outdated code patterns.

let texts = ["1", "x", "3"]
let numbers = texts.flatMap { Int($0) }

Fix: Use compactMap when the transformation returns an optional and you want only successful results.

let texts = ["1", "x", "3"]
let numbers = texts.compactMap { Int($0) }

The corrected version works because compactMap is the modern and intended API for removing nil results from collection transformations.

Mistake 4: Returning the wrong shape from flatMap

flatMap is for flattening one level of nested sequences. If the closure does not return a sequence, it is usually the wrong method.

Problem: When the closure returns a single value instead of a nested collection, the code may fail to compile or express the wrong intention.

let numbers = [1, 2, 3]
let doubled = numbers.flatMap { $0 * 2 }

Fix: Use map when you are transforming each element into one new element.

let numbers = [1, 2, 3]
let doubled = numbers.map { $0 * 2 }

The corrected version works because map is designed for one-to-one element transformation.

7. Best Practices

Practice 1: Choose the method based on the result shape

A simple way to choose correctly is to ask what kind of result you want at the end.

// Many values become one value
let sum = [1, 2, 3].reduce(0, +)

// Values convert, invalid ones are removed
let ints = ["1", "x", "3"].compactMap { Int($0) }

// Nested arrays become one flat array
let merged = [[1], [2, 3]].flatMap { $0 }

This works well because the method matches the intended output shape instead of just the input type.

Practice 2: Prefer clear closure names when the operation is not obvious

Shorthand syntax like $0 is fine for small expressions, but named parameters are often easier to read in longer closures.

let prices = ["9.99", "oops", "14.50"]

let total = prices
    .compactMap { priceText in
        Double(priceText)
    }
    .reduce(0.0) { runningTotal, price in
        runningTotal + price
    }

This is easier to maintain because each closure argument explains its role.

Practice 3: Keep reduce focused on accumulation

reduce is best when each step updates one accumulated result. If the closure becomes hard to read, consider a loop or a different method.

let words = ["Swift", "is", "fun"]
let sentence = words.reduce("") { result, word in
    result.isEmpty ? word : result + " " + word
}

This is a reasonable use of reduce because the result remains one growing string.

8. Limitations and Edge Cases

9. Practical Mini Project

In this mini project, you will process order data stored as nested arrays of price strings. The goal is to flatten the batches, convert valid prices, and compute a final total.

let orderBatches = [
    ["12.50", "9.99", "invalid"],
    ["5.00", "7.25"],
    ["error", "14.75"]
]

let allPrices = orderBatches.flatMap { $0 }
let validPrices = allPrices.compactMap { Double($0) }
let totalRevenue = validPrices.reduce(0.0) { sum, price in
    sum + price
}

print("All prices: \(allPrices)")
print("Valid prices: \(validPrices)")
print("Total revenue: \(totalRevenue)")

This example shows the three methods working together in a realistic sequence. flatMap removes one level of nesting, compactMap filters out invalid values during conversion, and reduce creates the final total.

10. Key Points

11. Practice Exercise

Try this exercise to confirm that you can tell these methods apart.

Expected output: a flat array of strings, an array of valid integers, and a final total.

Hint: Use flatMap first, then compactMap, then reduce.

let scoreGroups = [
    ["10", "20"],
    ["x", "15"],
    ["25"]
]

let flatScores = scoreGroups.flatMap { $0 }
let validScores = flatScores.compactMap { Int($0) }
let total = validScores.reduce(0) { sum, score in
    sum + score
}

print("Flat scores: \(flatScores)")
print("Valid scores: \(validScores)")
print("Total: \(total)")

12. Final Summary

reduce, compactMap, and flatMap are all transformation tools, but each solves a different problem. reduce creates one result from many elements, compactMap keeps only successful transformed values, and flatMap flattens nested sequences by one level. Once you recognize the output shape you need, choosing the right method becomes much easier.

As you continue learning Swift collections, compare these methods with map, filter, and sorted. Those methods, together with the three covered here, form a core set of tools for writing expressive and maintainable collection code.