# Swift map, filter, reduce: How to Transform Collections

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

`map`, `filter`, and `reduce` are three of the most useful higher-order functions in Swift. They let you transform, select, and combine values in collections like arrays without writing manual loops, which makes code shorter, clearer, and often easier to reason about.

> **Quick answer:** Use `map` when you want to turn every element into a new value, `filter` when you want to keep only matching elements, and `reduce` when you want to combine all elements into one final result.

> **Difficulty:** Beginner
>
> **Helpful to know first:** You will understand this better if you already know basic Swift syntax, arrays, constants and variables, and how closures work at a simple level.

## 1. What Is map, filter, reduce?

In Swift, these are standard library methods available on sequences and collections. They are called higher-order functions because they take a closure as an argument.

- `map` transforms each element into a new element.
- `filter` keeps only the elements that match a condition.
- `reduce` combines all elements into one accumulated result.

They are often discussed together because they solve related collection-processing tasks. If you are deciding between them, think about the result you want:

- If you want another collection of the same length, start with `map`.
- If you want fewer elements based on a test, use `filter`.
- If you want one final value such as a sum, string, dictionary, or count, use `reduce`.

These methods do not usually mutate the original array. Instead, they return new values, which fits well with Swift's emphasis on clear and safe value handling.

## 2. Why map, filter, reduce Matters

These methods matter because collection processing is everywhere in Swift programs. You may need to convert model data into display text, remove invalid values, total prices in a shopping cart, or build lookup structures from arrays.

Compared with manual `for` loops, they often express intent more directly:

- `map` says “transform this data.”
- `filter` says “keep only these items.”
- `reduce` says “combine everything into one result.”

This makes code easier to read, especially when the operation itself is simple and the method name describes the goal.

> These functions are not always better than a loop. If the logic is complex, has many side effects, or needs early exit, a regular loop may be clearer.

## 3. Basic Syntax

### Using map

The closure passed to `map` receives one element and must return the transformed value.

```swift
let numbers = [1, 2, 3]
let doubled = numbers.map { number in
    number * 2
}
```

Here, each number is multiplied by 2, so the result is a new array containing `[2, 4, 6]`.

### Using filter

The closure passed to `filter` must return a Boolean. `true` means keep the element, and `false` means discard it.

```swift
let numbers = [1, 2, 3, 4]
let evens = numbers.filter { number in
    number % 2 == 0
}
```

This returns only the even numbers, so the result is `[2, 4]`.

### Using reduce

`reduce` takes an initial value and a closure. The closure receives the current accumulated value and the next element, then returns the updated accumulated value.

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

The initial value is `0`. Swift adds each number to it, so the final result is `10`.

## 4. Step-by-Step Examples

### Example 1: Converting temperatures with map

This example converts Celsius values into Fahrenheit values. Every input value becomes one output value, so `map` is the right choice.

```swift
let celsiusValues = [0.0, 20.0, 30.0]

let fahrenheitValues = celsiusValues.map { celsius in
    (celsius * 9 / 5) + 32
}

print(fahrenheitValues)
```

Output (tested with Swift 6.4):

```text
[32.0, 68.0, 86.0]
```

The output is an array of converted temperatures. The original array is unchanged.

### Example 2: Keeping passing scores with filter

Here we want only scores that meet a passing threshold. Some elements stay, and some are removed, so `filter` fits naturally.

```swift
let scores = [42, 75, 90, 58, 81]

let passingScores = scores.filter { score in
    score >= 60
}

print(passingScores)
```

Output (tested with Swift 6.4):

```text
[75, 90, 81]
```

The result contains only the scores that pass, which would be `[75, 90, 81]`.

### Example 3: Calculating a total with reduce

A shopping cart total is a classic `reduce` task. We want one final number from many prices.

```swift
let prices = [19.99, 5.50, 3.25]

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

print(total)
```

Output (tested with Swift 6.4):

```text
28.74
```

The closure adds each price to the running total until one final value remains.

### Example 4: Chaining map, filter, and reduce

These methods become especially powerful when combined. In this example, we keep even numbers, square them, and then add them.

```swift
let numbers = [1, 2, 3, 4, 5, 6]

let result = numbers
    .filter { number in
        number % 2 == 0
    }
    .map { number in
        number * number
    }
    .reduce(0) { sum, number in
        sum + number
    }

print(result)
```

Output (tested with Swift 6.4):

```text
56
```

The even numbers are `[2, 4, 6]`, their squares are `[4, 16, 36]`, and the final sum is `56`.

## 5. Practical Use Cases

- Use `map` to convert model objects into display strings for user interfaces.
- Use `filter` to remove invalid, hidden, archived, or expired items from a list.
- Use `reduce` to calculate totals, counts, averages, or concatenated text.
- Use chained operations to clean and transform API data before displaying or storing it.
- Use `map` to extract one property from many objects, such as all usernames from an array of users.
- Use `filter` before expensive work so you process only relevant items.
- Use `reduce` to build dictionaries or grouped summaries from arrays.

## 6. Common Mistakes

### Mistake 1: Using map when you really want filter

Beginners often use `map` when they want to remove elements. But `map` transforms every element and keeps the same number of elements.

**Problem:** This code returns Boolean values instead of returning only the passing scores, because `map` transforms each element rather than selecting matching ones.

```swift
let scores = [40, 72, 88]
let passingScores = scores.map { score in
    score >= 60
}
```

**Fix:** Use `filter` when the goal is to keep only elements that match a condition.

```swift
let scores = [40, 72, 88]
let passingScores = scores.filter { score in
    score >= 60
}
```

The corrected version works because `filter` removes elements that do not satisfy the condition.

### Mistake 2: Forgetting that reduce needs the correct initial value

The initial value in `reduce` affects both the type and the result. A wrong starting value can produce incorrect output.

**Problem:** This code starts the sum at `10`, so the final result is larger than expected even though the closure itself is correct.

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

**Fix:** Choose an initial value that matches the identity of the operation, such as `0` for addition or an empty string for concatenation.

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

The corrected version works because the accumulation starts from the proper neutral value.

### Mistake 3: Forgetting to return the right type from map

The closure in `map` must return the transformed value for each element. If you return the wrong type, Swift reports a type mismatch.

**Problem:** This code tries to create strings from numbers but actually returns integers, which can lead to an error like `Cannot convert value of type 'Int' to closure result type 'String'`.

```swift
let numbers = [1, 2, 3]
let labels: [String] = numbers.map { number in
    number * 2
}
```

**Fix:** Return the type you actually want each output element to have.

```swift
let numbers = [1, 2, 3]
let labels: [String] = numbers.map { number in
    "Value: \(number * 2)"
}
```

The corrected version works because each element is transformed into a `String`, matching the declared result type.

### Mistake 4: Using reduce for everything

`reduce` is powerful, but it can make simple transformations harder to read when used unnecessarily.

**Problem:** This code uses `reduce` to build an array of doubled values, which is possible but less clear than using `map`.

```swift
let numbers = [1, 2, 3]
let doubled = numbers.reduce([]) { partialResult, number in
    partialResult + [number * 2]
}
```

**Fix:** Use the method that directly matches your intent. For one-to-one transformation, `map` is clearer.

```swift
let numbers = [1, 2, 3]
let doubled = numbers.map { number in
    number * 2
}
```

The corrected version works because `map` communicates the transformation directly and more simply.

## 7. Best Practices

### Choose the method that matches the result you want

A direct match between method and intent makes code easier to read. If you want one output per element, prefer `map` over forcing the logic into `reduce`.

```swift
// Less preferred for simple transformation
let uppercased1 = ["a", "b"].reduce([]) { result, item in
    result + [item.uppercased()]
}

// Preferred
let uppercased2 = ["a", "b"].map { item in
    item.uppercased()
}
```

The preferred version is easier to scan because the method name already tells the reader what is happening.

### Keep closures short and focused

If a closure becomes long or hard to understand, readability drops quickly. A short closure makes chained operations much easier to maintain.

```swift
import Foundation

let names = [" alice ", " BOB "]

let cleanNames = names.map { name in
    name.trimmingCharacters(in: .whitespaces).lowercased()
}
```

This closure is still compact enough to understand quickly. If it grows larger, consider extracting a helper function.

### Use named helper functions when the intent is reusable

A named function can be clearer than repeating the same closure in multiple places.

```swift
func isAdult(age: Int) -> Bool {
    age >= 18
}

let ages = [12, 19, 30]
let adults = ages.filter(isAdult)
```

This is useful when the rule has a meaningful name and might be reused in more than one place.

## 8. Limitations and Edge Cases

- `map` does not remove `nil` values from optionals. If you are transforming optional results and want to skip `nil`, `compactMap` is often the correct tool.
- `filter` preserves the original order of matching elements. It does not sort them.
- `reduce` can become hard to read when the accumulation logic is complex. In those cases, a loop may be easier to understand.
- Long chains of collection operations can create intermediate arrays. For typical app code this is usually fine, but performance-sensitive code may need closer inspection.
- These methods return new values rather than changing the original array in place, which can surprise beginners expecting mutation.
- If your closure has side effects, such as logging, writing files, or mutating outside state, the code may become harder to reason about even if it compiles.

> A common comparison is `map` versus `compactMap`. `map` transforms every element and keeps optionals if you return them. `compactMap` transforms and removes `nil` results.

## 9. Practical Mini Project

Let’s build a small report from a list of quiz scores. We will use `filter` to keep passing scores, `map` to create labels, and `reduce` to calculate the total.

```swift
let scores = [45, 82, 67, 91, 58]

let passingScores = scores.filter { score in
    score >= 60
}

let labels = passingScores.map { score in
    "Passing score: \(score)"
}

let totalPassingScore = passingScores.reduce(0) { sum, score in
    sum + score
}

print("Original scores: \(scores)")
print("Passing scores: \(passingScores)")
print("Labels: \(labels)")
print("Total of passing scores: \(totalPassingScore)")
```

Output (tested with Swift 6.4):

```text
Original scores: [45, 82, 67, 91, 58]
Passing scores: [82, 67, 91]
Labels: ["Passing score: 82", "Passing score: 67", "Passing score: 91"]
Total of passing scores: 240
```

This mini project shows a realistic workflow: first select the data you want, then transform it into a presentation-friendly format, and finally compute a summary value from it.

## 10. Key Points

- `map` transforms each element into a new value.
- `filter` keeps only elements that satisfy a condition.
- `reduce` combines all elements into one result.
- These methods usually return new values instead of mutating the original collection.
- Choose the method based on the shape of the result you want.
- Use chaining carefully so the code stays readable.
- Consider `compactMap` when you need to transform values and discard `nil` results.

## 11. Practice Exercise

Try this exercise to check your understanding.

- Start with an array of product prices: `[12.0, 25.0, 8.0, 30.0, 15.0]`.
- Use `filter` to keep only prices greater than or equal to 15.
- Use `map` to convert those prices into strings like `"$25.0"`.
- Use `reduce` to calculate the total of the filtered prices.

**Expected output:** an array of matching price labels and the total of the matching prices.

**Hint:** Do the filtering first, then transform and total the filtered results.

```swift
let prices = [12.0, 25.0, 8.0, 30.0, 15.0]

let filteredPrices = prices.filter { price in
    price >= 15.0
}

let priceLabels = filteredPrices.map { price in
    "$\(price)"
}

let total = filteredPrices.reduce(0.0) { sum, price in
    sum + price
}

print(priceLabels)
print(total)
```

Output (tested with Swift 6.4):

```text
["$25.0", "$30.0", "$15.0"]
70.0
```

## 12. Final Summary

`map`, `filter`, and `reduce` are foundational Swift tools for working with collections. They let you express common tasks clearly: transform every value, keep only matching values, or combine everything into one final result. Once you understand the shape of each result, choosing the right one becomes much easier.

For beginners, the biggest improvement usually comes from learning to match the method to the problem instead of reaching for loops or forcing everything into `reduce`. For intermediate Swift developers, the next step is writing smaller, clearer closures and recognizing when related methods like `compactMap` or a plain `for` loop are the better fit.

A strong next step is to learn `compactMap`, `flatMap`, and closure shorthand syntax so you can read and write more expressive Swift collection code.
