map, compactMap and flatMap

Introduction

There is map as a typical operation for collections and so-called" guys with values in something ". However, there are variants of map, which can be confusing, so I'd like to organize them.

In this article, an array is used as an example. In fact, arrays, dictionaries, and optional "containers" provide similar operations (though not exactly the same).

map type

map Transforms each element in the array. Since all elements are converted, the number of elements does not change before and after conversion.

map.png

compactMap Same as map, but excludes nil from the elements and unwraps Optional. Since nil is excluded, the number of elements may change (decrease) before and after conversion unlike map.

compactMap.png

flatMap If the array is nested, extract the elements from the inner array into a flat array (2D array-> 1D array).

flatMap1.png

If you think of the inner "** container ** called array" as "** container ** called Optional", you will create an "array with the contents of Optional extracted", which is the same operation as compactMap.

--Array <Array <element >>-(conversion)-> Array <element> --Array <Optional <element >>-(conversion)-> Array <element>

flatMap2.png

Early versions of Swift, which did not implement compactMap, were also used for this purpose, but are now deprecated. Let's use compactMap obediently.

sample

This is a sample using map, compactMap, flatMap. Please read it while comparing it with the above four figures.

Code example

import Foundation

enum Category: String, CustomStringConvertible {
    var description: String {
        self.rawValue
    }

    case personal
    case business
}

struct Item: CustomStringConvertible {
    var description: String {
        """
        name: "\(self.name)", price: \(self.price), categories: \(self.categories ?? [])

        """
    }

    let name: String
    let price: Int
    let categories: [Category]?
}

let items: [Item] = [
    Item(name: "Suit", price: 15000, categories: [.business]),
    Item(name: "Pen", price: 400, categories: [.personal, .business]),
    Item(name: "Sea", price: 99999, categories: nil),
    Item(name: "Drink", price: 120, categories: [.personal]),
    Item(name: "Sky", price: 99999, categories:nil),
    Item(name: "Comic", price: 600, categories: [.personal])
]

print("""
      == Items ==========
      \(items)

      """
)

// map transforms each element in an Array.
let map = items.map { item in
    item.categories ?? []
}
print("""
      == map "item.categories ?? []" ==========
      \(map)

      """
)

// compactMap is a map that only collect non-nil values.
let compact = items.compactMap { item in
    item.categories
}
print("""
      == compactMap "item.categories" ==========
      \(compact)

      """
)

// flatMap flattens the inner Array.
let flat1 = items.flatMap { item in
    item.categories ?? []
}
print("""
      == flatMap "item.categories ?? []" ==========
      \(flat1)

      """
)

// This type of flatMap is deprecated. You should use compactMap.
let flat2 = items.flatMap { item in
    item.categories
}
print("""
      == flatMap "item.categories" ==========
      \(flat2)

      """
)

Execution result

== Items ==========
[name: "Suit", price: 15000, categories: [business]
, name: "Pen", price: 400, categories: [personal, business]
, name: "Sea", price: 99999, categories: []
, name: "Drink", price: 120, categories: [personal]
, name: "Sky", price: 99999, categories: []
, name: "Comic", price: 600, categories: [personal]
]

== map "item.categories ?? []" ==========
[[business], [personal, business], [], [personal], [], [personal]]

== compactMap "item.categories" ==========
[[business], [personal, business], [personal], [personal]]

== flatMap "item.categories ?? []" ==========
[business, personal, business, personal, personal]

== flatMap "item.categories" ==========
[[business], [personal, business], [personal], [personal]]

Summary

It's easy to understand map if you show it in a diagram. Operations around here are often used in the Combine framework, so I think it will be very easy to develop if you can master it.

Recommended Posts

map, compactMap and flatMap
Difference between Stream map and flatMap
Toward understanding of map and flatmap in Stream (1)
Use swift Filter and Map
[Ruby] Simplify each using map and inject
Try to realize Scala-like Option type pattern matching and map, flatMap in Java