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.
- reduce combines all elements into one final value.
- compactMap transforms each element and removes any nil results.
- flatMap transforms each element into a sequence, then flattens the nested result by one level.
- All three are often used with arrays, but they work with many Swift sequence and collection types.
- They are commonly confused with map, which transforms elements but keeps the same number of results.
In practical terms, think of them like this:
- reduce: “Turn many values into one.”
- compactMap: “Convert values, but throw away failures.”
- flatMap: “Merge nested lists into one list.”
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:
- Adding prices from a shopping cart
- Converting strings like ["1", "2", "x"] into valid integers
- Flattening grouped data such as arrays of arrays
- Building dictionaries, strings, or summaries from many values
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
- Use reduce to total cart prices, count characters, build grouped summaries, or combine values into a dictionary.
- Use compactMap to parse user input, filter invalid API fields, or load only valid IDs from text data.
- Use flatMap to flatten nested arrays such as sections of items, batches of records, or grouped tags.
- Use compactMap followed by reduce when some source values may fail conversion but the remaining valid values should still be processed.
- Use reduce when you need one final summary object rather than another array.
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
- flatMap flattens only one level. If your data is nested more deeply, you may need repeated flattening or a custom solution.
- compactMap removes nil results, which means you lose information about which original elements failed conversion.
- reduce can become hard to read if the accumulator logic is too complex.
- If you see code online using flatMap to remove nil values from a collection, it may be written for older Swift versions.
- A common “not working” scenario is choosing flatMap when map or compactMap is actually needed. The clue is the closure result: one value, optional value, or sequence.
- You may encounter compiler messages such as Cannot convert value of type when the closure return type does not match what the method expects.
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
- reduce combines a collection into one final value.
- compactMap transforms elements and removes nil results.
- flatMap flattens one level of nested sequences.
- map is different because it transforms elements without removing failures or flattening nesting.
- The best method depends on the shape of the result you want.
- Modern Swift uses compactMap, not flatMap, for optional-removal in collections.
11. Practice Exercise
Try this exercise to confirm that you can tell these methods apart.
- Start with a nested array of score strings.
- Flatten the nested array into one array.
- Convert valid score strings to integers.
- Calculate the total score.
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.