Swift map, filter, and sorted: Transforming Collections
Swift collections often need to be transformed before you can display, store, or process their data. The three most common tools for that job are map, filter, and sorted. In this article, you will learn what each method does, how their closures work, when to use one instead of another, and how to avoid the mistakes beginners often make.
Quick answer: Use map to convert every element into a new value, filter to keep only elements that match a condition, and sorted to return a new collection ordered by a comparison rule. All three return new collections instead of changing the original one.
Difficulty: Beginner
Helpful to know first: You will understand this better if you already know basic Swift syntax, arrays, closures, and simple types like String, Int, and Bool.
1. What Is map, filter, and sorted?
map, filter, and sorted are standard library methods used to create new collections from existing ones.
- map transforms each element into a new value.
- filter keeps only elements that pass a test.
- sorted returns elements in a chosen order.
- They are commonly used with arrays, but they also work with many other Swift collection types.
- They do not usually mutate the original collection, which makes code easier to reason about.
These methods solve a common problem: taking a list of values and producing a cleaner, more useful version of that list without writing a manual loop every time.
They are also naturally compared with each other because they answer different questions:
- map: “How should each value change?”
- filter: “Which values should stay?”
- sorted: “In what order should the values appear?”
2. Why map, filter, and sorted Matters
These methods matter because collection processing is everywhere in Swift programs. You might fetch data from an API, load user input, read a file, or build a menu for display. In each case, the raw data is often not in the exact form you need.
Instead of writing long loops with temporary variables, you can express your intent directly:
- Convert product prices from integers to formatted strings with map.
- Keep only active users with filter.
- Display names alphabetically with sorted.
This style has practical benefits:
- Code is shorter and easier to read.
- The purpose of the code is clearer at a glance.
- You reduce the chance of loop-related bugs, such as forgetting to append or update a value.
- Chaining transformations can produce very expressive code.
That said, these methods are not always the best choice. If you need complex stateful logic, early exits, or performance-sensitive mutation of the same array, a loop may sometimes be clearer.
3. Basic Syntax or Core Idea
Each of these methods takes a closure. The closure explains how to transform, test, or compare elements.
Using map
This example converts numbers into strings.
let numbers = [1, 2, 3]
let labels = numbers.map { number in
"Item \(number)"
}
Here, the closure runs once for every element. For each number, it returns a new string, so the result is an array of strings.
Using filter
This example keeps only even numbers.
let numbers = [1, 2, 3, 4, 5]
let evenNumbers = numbers.filter { number in
number % 2 == 0
}
The closure must return true or false. Elements that return true stay in the new array.
Using sorted
This example sorts numbers from smallest to largest.
let numbers = [5, 2, 8, 1]
let sortedNumbers = numbers.sorted()
For values that already know how to compare themselves, such as Int and String, you can call sorted() with no closure.
For custom ordering, pass a closure:
let names = ["Mia", "Alex", "Charlotte"]
let byLength = names.sorted { first, second in
first.count < second.count
}
The comparison closure for sorted receives two values and must return whether the first should come before the second.
4. Step-by-Step Examples
Example 1: Convert temperatures with map
Suppose you have temperatures in Celsius and want a new array in Fahrenheit.
let celsiusValues = [0.0, 20.0, 30.0]
let fahrenheitValues = celsiusValues.map { celsius in
(celsius * 9 / 5) + 32
}
This creates a new array where each original value has been transformed. The original celsiusValues array stays unchanged.
Example 2: Keep passing scores with filter
Now imagine a list of exam scores where only passing values should remain.
let scores = [45, 88, 62, 30, 91]
let passingScores = scores.filter { score in
score >= 50
}
The closure checks each score and keeps only the ones that meet the condition. This is more direct than writing a loop and appending values manually.
Example 3: Sort names alphabetically with sorted
Sorting strings is one of the most common uses of sorted.
let names = ["Zoe", "Adam", "Lina"]
let alphabeticalNames = names.sorted()
The result is a new array ordered alphabetically. This is the simplest form because String already supports standard comparison.
Example 4: Sort products by price
For custom types, you usually provide a comparison closure.
struct Product {
let name: String
let price: Double
}
let products = [
Product(name: "Keyboard", price: 99.0),
Product(name: "Mouse", price: 49.0),
Product(name: "Monitor", price: 199.0)
]
let cheapestFirst = products.sorted { first, second in
first.price < second.price
}
This sorts the product array by the price property. The comparison closure makes the sorting rule explicit.
Example 5: Chain map, filter, and sorted together
These methods become especially powerful when combined.
let numbers = [8, 3, 12, 5, 10]
let result = numbers
.filter { $0 >= 5 }
.map { $0 * 2 }
.sorted()
This code first keeps values greater than or equal to 5, then doubles each remaining value, then sorts the final result. Chaining works well when each step has one clear purpose.
5. Practical Use Cases
- Convert arrays of model data into display strings using map.
- Remove invalid or empty user input values with filter.
- Order products, tasks, or messages by date, price, or priority with sorted.
- Create a cleaned and ordered report by chaining all three methods together.
- Prepare API response data for UI display without manually looping through every item.
- Build search results by filtering matching items and sorting them by relevance or name.
6. Common Mistakes
Mistake 1: Using map when you really need filter
Beginners sometimes try to use map to remove values. But map transforms every element and does not decide whether an element should stay or disappear.
Problem: This code creates an array of booleans instead of keeping only even numbers, because map transforms each element into the result of the condition.
let numbers = [1, 2, 3, 4]
let evens = numbers.map { $0 % 2 == 0 }
Fix: Use filter when the closure returns a condition that decides whether to keep an element.
let numbers = [1, 2, 3, 4]
let evens = numbers.filter { $0 % 2 == 0 }
The corrected version works because filter keeps only the elements for which the closure returns true.
Mistake 2: Assuming sorted changes the original array
sorted returns a new array. It does not rearrange the original collection in place.
Problem: This code calls sorted() and then ignores the returned array, so the original array stays in the same order.
var scores = [30, 10, 20]
scores.sorted()
print(scores)
Fix: Assign the result back, or use sort() if you intentionally want to mutate the array in place.
var scores = [30, 10, 20]
scores = scores.sorted()
print(scores)
The corrected version works because the new sorted array is stored and used.
Mistake 3: Returning the wrong type from a closure
Each method expects a different closure result. map can return any transformed value, filter must return a Boolean, and sorted must return a Boolean comparison between two values.
Problem: This code returns an Int from a filter closure, which causes a compile-time type mismatch.
let numbers = [1, 2, 3]
let result = numbers.filter { number in
number * 2
}
Fix: Return a Boolean condition from filter, such as whether the number is greater than a threshold.
let numbers = [1, 2, 3]
let result = numbers.filter { number in
number > 1
}
The corrected version works because filter receives the true-or-false test it requires.
Mistake 4: Confusing sorted() with sort()
This is a very common Swift confusion. The names are similar, but their behavior is different.
Problem: This code tries to call the mutating sort() method on a constant array, which produces an error because the array cannot be modified.
let names = ["Sam", "Ava", "Ben"]
names.sort()
Fix: Use sorted() when you want a new array, or declare the array with var if you truly need in-place sorting.
let names = ["Sam", "Ava", "Ben"]
let sortedNames = names.sorted()
The corrected version works because sorted() does not require the original array to be mutable.
7. Best Practices
Practice 1: Pick the method that matches your intent
When someone reads your code, they should understand immediately whether you are transforming, filtering, or ordering data. Using the correct method makes that intent obvious.
Less clear:
var result: [Int] = []
for number in [1, 2, 3] {
result.append(number * 2)
}
Preferred:
let result = [1, 2, 3].map { $0 * 2 }
The preferred version is shorter and clearly communicates that every element is being transformed.
Practice 2: Keep each closure focused on one simple idea
Closures are easiest to read when they do one thing. If the logic becomes too long, split it into multiple steps or a named function.
Less preferred:
let values = [3, 7, 2, 9]
let result = values.filter { number in
if number > 5 {
return true
} else {
return false
}
}
Preferred:
let values = [3, 7, 2, 9]
let result = values.filter { $0 > 5 }
The preferred version is easier to scan and reduces unnecessary noise.
Practice 3: Use chained transformations carefully
Chaining is powerful, but too many steps in one expression can become hard to debug. Keep chains readable and line-break them when needed.
Less preferred:
let finalValues = [12, 5, 18, 3].filter { $0 > 4 }.map { $0 * 10 }.sorted()
Preferred:
let finalValues = [12, 5, 18, 3]
.filter { $0 > 4 }
.map { $0 * 10 }
.sorted()
The preferred version keeps the transformation pipeline clear and easier to maintain.
8. Limitations and Edge Cases
- map does not remove elements. If you need to skip values entirely, use filter or consider compactMap when working with optionals.
- filter keeps the original element type. It does not transform values into a different type.
- sorted() without a closure works only when the element type supports comparison, such as Int or String.
- Sorting custom types usually requires a closure like { $0.price < $1.price }.
- sorted returns a new array, even when the original collection is another type. That can matter if you expected in-place mutation.
- If you chain many transformations on very large collections, readability and performance may both deserve closer attention.
- A common “not working” case is trying to sort a custom struct without telling Swift how two values should be compared.
A related method you will often see is compactMap. It transforms elements like map, but also removes nil results. That is useful when converting strings to numbers, for example.
9. Practical Mini Project
Let’s build a small example that processes student scores. We will filter out failing scores, convert the remaining scores into label strings, and sort them from highest to lowest.
let scores = [72, 48, 91, 63, 55]
let report = scores
.filter { $0 >= 50 }
.sorted { $0 > $1 }
.map { "Passed with score: \($0)" }
for line in report {
print(line)
}
This mini project shows all three methods working together:
- filter removes failing scores.
- sorted orders the passing scores from highest to lowest.
- map converts the numbers into readable output strings.
The result is a clean processing pipeline that reads almost like a description of the task itself.
10. Key Points
- map transforms every element into a new value.
- filter keeps only elements that match a Boolean condition.
- sorted returns a new array in sorted order.
- sorted() does not mutate the original collection; sort() does.
- The closure type matters: transform for map, test for filter, compare for sorted.
- These methods can be chained to create clear, expressive collection-processing code.
11. Practice Exercise
Try this exercise to confirm you understand how the three methods differ.
- Start with an array of names: ["Olivia", "Max", "Sophia", "Leo"].
- Use filter to keep only names with more than 3 characters.
- Use sorted to arrange the remaining names alphabetically.
- Use map to convert each name into a greeting like "Hello, Olivia!".
Expected output: An array containing greeting strings for the filtered and sorted names.
Hint: Think about the order of operations. It is usually easier to filter first, sort second, and transform last.
let names = ["Olivia", "Max", "Sophia", "Leo"]
let greetings = names
.filter { $0.count > 3 }
.sorted()
.map { "Hello, \($0)!" }
print(greetings)
12. Final Summary
map, filter, and sorted are some of the most useful collection methods in Swift. They let you transform data, remove unwanted elements, and control ordering with concise, readable code. Once you understand the purpose of each method, many common loops become much simpler to write and easier to understand.
The most important habit is choosing the right tool for the job: map changes values, filter selects values, and sorted orders values. After you are comfortable with these, a strong next step is learning related methods such as compactMap, reduce, and forEach so you can handle even more collection-processing tasks in Swift.