# Swift flatMap vs compactMap: How to Transform Collections

Source: https://devdocs10.com/articles/swift/swift-flatmap-compactmap  
By Murat Bo · Updated 2026-10-17 · DevDocs10 (https://devdocs10.com)

`flatMap` and `compactMap` are two important Swift higher-order functions used to transform collection data. They look similar at first, but they solve different problems: one is for flattening nested results, and the other is for removing `nil` values while transforming. Understanding the difference helps you write cleaner, safer, and more expressive Swift code.

> **Quick answer:** Use `compactMap` when your transformation might return `nil` and you want a result with only non-optional values. Use `flatMap` when each element produces a sequence or collection and you want the nested results flattened into a single collection.

> **Difficulty:** Beginner
>
> **Helpful to know first:** basic Swift syntax, arrays, optionals, closures, and how `map` transforms values in a collection.

## 1. What Are flatMap and compactMap?

Both methods are used on sequences such as arrays, but they produce different kinds of results.

- `map` transforms each element and keeps the same overall structure.
- `compactMap` transforms each element and discards any `nil` results.
- `flatMap` transforms each element into a sequence, then flattens one level of nesting.
- They are often confused because older Swift versions used `flatMap` in some optional-removal situations that now belong to `compactMap`.

For example, if you have an array of strings and want to convert them to integers, some conversions may fail and return `nil`. That is a natural job for `compactMap`. If you have an array of arrays and want one flat array, that is a natural job for `flatMap`.

## 2. Why flatMap and compactMap Matter

Real programs often deal with incomplete or nested data. User input may contain invalid values. File or API data may contain empty entries. Collections may contain other collections. These methods help you handle those cases clearly without writing verbose loops.

They matter because they let you:

- convert values while keeping code concise
- remove invalid or missing results safely
- flatten nested collections without manual loops
- express intent more clearly than custom filtering and appending code

> If you choose the wrong method, your code may produce the wrong shape of data, such as an array of optionals instead of plain values, or a nested array when you expected a flat one.

## 3. Basic Syntax and Core Idea

### Using compactMap

Use `compactMap` when the closure returns an optional. Swift keeps only the non-`nil` results.

```swift
let values = ["1", "2", "abc", "4"]
let numbers = values.compactMap { Int($0) }
```

This produces an array of integers. The string `"abc"` cannot become an `Int`, so its conversion returns `nil` and is dropped.

```swift
// Result: [1, 2, 4]
```

### Using flatMap

Use `flatMap` when each element becomes a collection or sequence and you want one combined result.

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

This takes a nested array and flattens it by one level.

```swift
// Result: [1, 2, 3, 4, 5]
```

### Swift flatMap vs compactMap

The key difference is in what the closure returns:

- If the closure returns an optional and you want to remove `nil`, use `compactMap`.
- If the closure returns a sequence or collection and you want to flatten one level, use `flatMap`.

## 4. Step-by-Step Examples

### Example 1: Convert valid strings to integers with compactMap

This is one of the most common beginner examples because it shows both transformation and optional removal clearly.

```swift
let rawScores = ["10", "25", "oops", "42"]
let scores = rawScores.compactMap { Int($0) }

print(scores)
```

Output (tested with Swift 6.4):

```text
[10, 25, 42]
```

The output is a clean array of integers with the invalid string removed.

```swift
// [10, 25, 42]
```

This works because `Int($0)` returns `Int?`, and `compactMap` keeps only successful conversions.

### Example 2: Flatten nested arrays with flatMap

Here each element is already an array. You want one array containing all inner values.

```swift
let weeklyTemps = [[21, 22], [19, 20], [23]]
let allTemps = weeklyTemps.flatMap { $0 }

print(allTemps)
```

Output (tested with Swift 6.4):

```text
[21, 22, 19, 20, 23]
```

The result is a single flat array.

```swift
// [21, 22, 19, 20, 23]
```

This is simpler than writing a loop and manually appending each inner array.

### Example 3: Extract tags from articles with flatMap

This example is more realistic. Suppose each article has multiple tags, and you want all tags in one list.

```swift
struct Article {
    let title: String
    let tags: [String]
}

let articles = [
    Article(title: "Swift Basics", tags: ["swift", "beginner"]),
    Article(title: "Optionals", tags: ["swift", "optionals"])
]

let allTags = articles.flatMap { $0.tags }

print(allTags)
```

Output (tested with Swift 6.4):

```text
["swift", "beginner", "swift", "optionals"]
```

The output contains every tag from every article in one array.

```swift
// ["swift", "beginner", "swift", "optionals"]
```

This is a common pattern when collecting nested values from model data.

### Example 4: Clean user input with compactMap

Suppose user input contains empty strings or invalid numbers. You can trim, validate, and keep only useful values.

```swift
import Foundation

let input = ["15", " ", "30", "abc"]

let cleanNumbers = input.compactMap { text in
    let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
    return Int(trimmed)
}

print(cleanNumbers)
```

Output (tested with Swift 6.4):

```text
[15, 30]
```

Only valid integers remain in the result.

```swift
// [15, 30]
```

This is a practical use of `compactMap` because invalid entries naturally become `nil`.

## 5. Practical Use Cases

- Parsing arrays of strings from forms, files, or APIs into numbers with `compactMap`.
- Removing missing values from optional properties collected from model objects.
- Flattening nested arrays such as grouped search results, menu sections, or batched responses with `flatMap`.
- Collecting child items from parent objects, such as tags from posts or permissions from user roles.
- Building a clean list of valid URLs, IDs, or dates from mixed raw input using `compactMap`.
- Combining one level of nested collection data before sorting, filtering, or counting.

## 6. Common Mistakes

### Mistake 1: Using map when compactMap is needed

This happens when the transformation returns an optional but you still use `map`. The result becomes an array of optionals instead of plain values.

**Problem:** This code keeps failed conversions as `nil`, so the result type is `[Int?]` instead of `[Int]`. That often leads to extra unwrapping later.

```swift
let items = ["1", "x", "3"]
let numbers = items.map { Int($0) }
```

**Fix:** Use `compactMap` when failed transformations should be removed from the result.

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

The corrected version works because `compactMap` removes `nil` values and returns a plain integer array.

### Mistake 2: Using compactMap when flatMap is needed

Some developers try to flatten nested arrays with `compactMap` because the names sound related. But `compactMap` does not flatten nested collections.

**Problem:** This code returns the same nested shape because there are no optional values to remove. It does not combine inner arrays into one array.

```swift
let groups = [[1, 2], [3, 4]]
let result = groups.compactMap { $0 }
```

**Fix:** Use `flatMap` when each element produces a collection and you want a single flattened result.

```swift
let groups = [[1, 2], [3, 4]]
let result = groups.flatMap { $0 }
```

The corrected version works because `flatMap` flattens one level of nested arrays.

### Mistake 3: Expecting compactMap to keep array positions

`compactMap` removes elements that become `nil`. That means the result may be shorter and indexes may shift.

**Problem:** If later code depends on the original positions, removing invalid elements can break that assumption and produce incorrect logic.

```swift
let raw = ["100", "bad", "300"]
let values = raw.compactMap { Int($0) }
print(values[1])
```

Output (tested with Swift 6.4):

```text
300
```

**Fix:** Keep the optional values with `map` if position matters, or store original indexes alongside the transformed values.

```swift
let raw = ["100", "bad", "300"]
let values = raw.map { Int($0) }
print(values[1] as Int?)
```

Output (tested with Swift 6.4):

```text
nil
```

The corrected version works because the array keeps the original number of elements, including failed conversions as `nil`.

### Mistake 4: Expecting flatMap to flatten multiple nested levels automatically

`flatMap` flattens one level at a time. It does not completely flatten deeply nested data in a single call.

**Problem:** If your data is nested more than one level, one call to `flatMap` may still leave nested arrays in the result.

```swift
let deeplyNested = [[[1, 2]], [[3]]]
let partlyFlat = deeplyNested.flatMap { $0 }
```

**Fix:** Apply another flattening step if the result is still nested.

```swift
let deeplyNested = [[[1, 2]], [[3]]]
let fullyFlat = deeplyNested.flatMap { $0 }.flatMap { $0 }
```

The corrected version works because each `flatMap` call removes only one level of nesting.

## 7. Best Practices

### Use compactMap when failure is normal and ignorable

If some values are expected to fail conversion and you simply want the valid results, `compactMap` is a clean choice.

```swift
let ids = ["10", "x", "30"]
let validIDs = ids.compactMap { Int($0) }
```

This keeps the code short and communicates that invalid values should be skipped.

### Use flatMap only when flattening is the real goal

If your closure returns collections and you want one combined collection, `flatMap` expresses that clearly.

```swift
let folders = [["a.txt", "b.txt"], ["c.txt"]]
let files = folders.flatMap { $0 }
```

This is better than using a more confusing transformation that hides the flattening intent.

### Choose map when you need to preserve shape

Sometimes removing values is not appropriate. If every input should correspond to one output position, `map` may be the better choice.

```swift
let rawValues = ["1", "bad", "3"]
let parsedValues = rawValues.map { Int($0) }
```

This preserves the relationship between source items and result positions.

### Prefer readable closures over clever shorthand

Short closures are fine, but if validation or cleanup logic grows, a named closure parameter can make the intent easier to read.

```swift
import Foundation

let rawInput = [" 8 ", "nine", "10"]

let numbers = rawInput.compactMap { text in
    let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
    return Int(trimmed)
}
```

This is easier to maintain than a dense one-line expression when the transformation becomes more complex.

## 8. Limitations and Edge Cases

- `flatMap` on sequences flattens one level only. Deeply nested structures need repeated flattening or a custom approach.
- `compactMap` removes failed values entirely, so the output count may be smaller than the input count.
- If you need to know why a conversion failed, `compactMap` may hide too much information because it only drops `nil` values.
- Older Swift explanations online may show `flatMap` where modern Swift uses `compactMap`. Be careful when reading outdated examples.
- If your closure returns the wrong type, Swift may report confusing type inference errors such as `Cannot convert value of type` messages. That usually means you chose the wrong transformation method.
- `compactMap` is not a replacement for validation with error reporting. It is best when silent skipping is acceptable.

## 9. Practical Mini Project

In this mini project, you will process a small set of order data. Each order stores item IDs as strings. Some IDs are invalid, and each order contains multiple items. The goal is to clean the IDs and then create one flat list of all valid item IDs.

```swift
import Foundation

struct Order {
    let customer: String
    let itemIDs: [String]
}

let orders = [
    Order(customer: "Ava", itemIDs: ["101", "102", "x"]),
    Order(customer: "Noah", itemIDs: ["205", " ", "206"]),
    Order(customer: "Mia", itemIDs: ["300", "bad"])
]

let validItemIDs = orders.flatMap { order in
    order.itemIDs.compactMap { rawID in
        let trimmed = rawID.trimmingCharacters(in: .whitespacesAndNewlines)
        return Int(trimmed)
    }
}

print(validItemIDs)
```

Output (tested with Swift 6.4):

```text
[101, 102, 205, 206, 300]
```

This example combines both methods naturally:

- `compactMap` converts valid item ID strings into integers and removes invalid ones.
- `flatMap` combines each order's cleaned item IDs into one final array.

The result is:

```swift
// [101, 102, 205, 206, 300]
```

This is a realistic pattern for cleaning nested data from forms, files, or network responses.

## 10. Key Points

- `compactMap` transforms values and removes any `nil` results.
- `flatMap` transforms values into collections and flattens one level.
- Use `map` when you want to preserve the original structure and count.
- `compactMap` is ideal for parsing valid values from mixed input.
- `flatMap` is ideal for turning nested collections into one collection.
- These methods are similar in style but solve different problems.
- Outdated Swift examples may use `flatMap` where modern code should use `compactMap`.

## 11. Practice Exercise

Try this exercise to check your understanding.

- Create an array of arrays of strings representing test scores.
- Some strings should be valid integers and some should be invalid, such as `"NA"`.
- Use `compactMap` to convert each inner array to valid integers only.
- Use `flatMap` to combine all valid scores into one array.
- Print the final array.

**Expected output:** one flat array containing only valid integer scores.

**Hint:** Clean each inner array first, then flatten the overall result.

```swift
let scoreGroups = [
    ["10", "20", "NA"],
    ["15", "oops", "25"],
    ["30", "40"]
]

let allScores = scoreGroups.flatMap { group in
    group.compactMap { Int($0) }
}

print(allScores)
```

Output (tested with Swift 6.4):

```text
[10, 20, 15, 25, 30, 40]
```

This solution works because each inner group is cleaned with `compactMap`, and then all cleaned groups are merged with `flatMap`.

## 12. Final Summary

`flatMap` and `compactMap` are closely related, but they are not interchangeable. `compactMap` is for transformations that may fail and return `nil`, while `flatMap` is for flattening one level of nested collections. Once you understand that difference, choosing the right one becomes much easier.

In Swift, these methods help you replace manual loops with code that is shorter and more expressive. They are especially useful when cleaning raw input, parsing values, and combining nested data structures. As a next step, compare them with `map` and `filter` so you can recognize exactly which transformation fits each problem.
