Map in Swift

Hey Swift enthusiasts! Today, we’re diving deep into one of Swift’s most powerful higher-order functions: map. If you’ve ever wanted to transform elements in a collection without breaking a sweat, you’re in for a treat!

At its core, map is a method that transforms each element in a collection using a provided closure, returning a new collection of the same type and size.

Let’s start with a simple example:

Swift
let numbers = [1, 2, 3, 4, 5]
let doubled = numbers.map { $0 * 2 }
print(doubled) 
// Output: [2, 4, 6, 8, 10]

Here, we’ve doubled each number in the array. Easy peasy!

Map isn’t just for simple types. Let’s use it with a custom struct:

Swift
struct User {
    let name: String
    let age: Int
}

let users = [
    User(name: "Alice", age: 25),
    User(name: "Bob", age: 30),
    User(name: "Charlie", age: 35)
]

let names = users.map { $0.name }
print(names) 
// Output: ["Alice", "Bob", "Charlie"]

You can chain multiple map calls for more complex transformations:

Swift
let ages = users.map { $0.age }
                .map { "\($0) years old" }
print(ages) 
// Output: ["25 years old", "30 years old", "35 years old"]

Swift’s map works great with optionals too:

Swift
let optionalNumber: Int? = 5
let doubledOptional = optionalNumber.map { $0 * 2 }
print(doubledOptional) 
// Output: Optional(10)

let noNumber: Int? = nil
let doubledNil = noNumber.map { $0 * 2 }
print(doubledNil) 
// Output: nil

Don’t forget, map works on dictionaries too:

Swift
let grades = ["Alice": 85, "Bob": 90, "Charlie": 78]
let scaledGrades = grades.mapValues { $0 * 1.1 }
print(scaledGrades) 
// Output: ["Alice": 93.5, "Bob": 99.0, "Charlie": 85.8]

When dealing with optionals in collections, compactMap is your friend:

Swift
let strings = ["1", "2", "three", "4", "five"]
let numbers = strings.compactMap { Int($0) }
print(numbers) // Output: [1, 2, 4]

Use flatMap when you want to flatten nested collections:

Swift
let nestedArrays = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
let flattened = nestedArrays.flatMap { $0 }
print(flattened) // Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]

Let’s look at a more practical example. Say you’re building a weather app:

Swift
struct WeatherData {
    let temperature: Double
    let condition: String
}

struct WeatherViewModel {
    let temperature: String
    let condition: String
}

let weatherData = [
    WeatherData(temperature: 25.5, condition: "Sunny"),
    WeatherData(temperature: 18.0, condition: "Cloudy"),
    WeatherData(temperature: 10.5, condition: "Rainy")
]

let viewModels = weatherData.map { data in
    WeatherViewModel(
        temperature: "\(Int(data.temperature))°C",
        condition: data.condition.uppercased()
    )
}

viewModels.forEach { vm in
    print("It's \(vm.temperature) and \(vm.condition)")
}
// Output:
// It's 25°C and SUNNY
// It's 18°C and CLOUDY
// It's 10°C and RAINY

Wrapping Up

As you can see, map is an incredibly versatile tool in your Swift toolbox. It allows you to transform collections with ease, whether you’re working with simple types, complex objects, optionals, or nested structures.

Remember, map doesn’t modify the original collection – it always returns a new one. This makes it perfect for functional programming patterns and maintaining immutability in your code.

So next time you find yourself writing a for-loop to transform a collection, ask yourself: “Could I use map here?” Chances are, you can – and your code will be all the cleaner for it!


Posted

in