SwiftのmapとflatMapがやっている事を理解する


便利だけど理解が難しい`map`と`flatMap`ですが、次のように捉えると自分としてはしっくりきました。
正確な表現ではないかもしれませんが、理解の参考になればと思います。

# 説明のための用語

`map`や`flatMap`が呼ばれるインスタンスを「レシーバ」と呼ぶことにします。

```swift
instance.map(closure)
instance.flatMap(closure)
```

と呼ぶ時の`instance`がレシーバです。


# `map`でやっていることを整理する

`map`は配列の各要素を変換して配列で返すイメージが強いと思いますが、
もう少し一般化して、次のようなステップを踏む処理だと捉えます。

#### 1. レシーバのジェネリクスを解除してクロージャに渡す。

ここで言う「ジェネリクスの解除」とは、オプショナルならアンラップ、SequenceTypeなら個々の要素ごとにバラす事を表します。

#### 2. 得られた値をクロージャ内の処理で変換する。

1で得られた値をクロージャに渡し、変換した値を返してもらいます。

#### 3. 変換結果に対して、レシーバの型に対応するジェネリクスを再設定する。

対応するジェネリクスとは、レシーバがOptionalならOptional、SequenceTypeならArrayになります。
つまり、2で受けとった値をオプショナルにしたり、値をまとめて配列にしたりします。

図で表すと次のようなイメージです。

![qiita_fegures.001.jpeg](https://qiita-image-store.s3.amazonaws.com/0/38775/544d8069-393d-c9f0-d758-1a176446b654.jpeg "qiita_fegures.001.jpeg")



# `flatMap`とは

`map`の2のステップの後、クロージャから渡された値に対して、
**ジェネリクスを解除してから3の処理に進むのが`flatMap`です。**

2と3の間で1と同じことをするイメージです。

![qiita_fegures.002.jpeg](https://qiita-image-store.s3.amazonaws.com/0/38775/f9a80b52-8d00-3c2e-1e7b-070df74199ff.jpeg "qiita_fegures.002.jpeg")

ここで、レシーバがSequenceTypeの場合、ジェネリクス解除ができないと（例えばnilをアンラップしようとする）、その値は返り値の配列から除外されます。

# 具体例

具体例を示します。
ここで使用している`Int(String)`の返り値は`Int?`であることを踏まえて読んでください。

## オプショナルの場合

```swift
let numberString: String? = "123"

let mapNumber = numberString.map { (s: String) -> Int? in
    return Int(s)
}
print(mapNumber) // -> Optional(Optional(123))

let flatMapNumber = numberString.flatMap { (s: String) -> Int? in
    return Int(s)
}
print(flatMapNumber) // -> Optional(123)
```

`map`と`flatMap`に渡しているクロージャは共通です。

`map`はクロージャの返り値のオプショナルをアンラップせずに3のステップに進むため、`map`の返り値は`Optional(Optional(Int))`です。
一方`flatMap`はアンラップするため、`flatMap`の返り値の型は`Optional<Int>`です。

## 配列の場合

### クロージャの返り値がオプショナル

```swift
let numberStrings: [String] = ["123", "456", "789"]

let mapNumbers = numberStrings.map { (s: String) -> Int? in
    return Int(s)
}
print(mapNumbers) // -> [Optional(123), Optional(456), Optional(789)]

let flatMapNumbers = numberStrings.flatMap { (s: String) -> Int? in
    return Int(s)
}
print(flatMapNumbers) // -> [123, 456, 789]
```

ここでも`map`と`flatMap`に渡しているクロージャは共通です。

`map`はクロージャの返り値のオプショナルをアンラップせずに3のステップに進むため、`map`の返り値は`[Int?]`です。
一方`flatMap`はアンラップするため、`flatMap`の返り値の型は`[Int]`です。

### クロージャの返り値がSequenceType

```swift
let numbers: [Int] = [123, 456, 789]

let mapNumbers = numbers.map { (number: Int) -> [Int] in
    return [number, number]
}
print(mapNumbers) // -> [[123, 123], [456, 456], [789, 789]]

let flatMapNumbers = numbers.flatMap { (number: Int) -> [Int] in
    return [number, number]
}
print(flatMapNumbers) // -> [123, 123, 456, 456, 789, 789]
```

内容は変わりましたが、`map`と`flatMap`に渡しているクロージャは共通です。

`map`はクロージャから配列を受け取り、そのまま3のステップに進むので、`map`の返り値は`[[Int]]`です。
一方`flatMap`は、クロージャから配列を受け取り、個々の要素に分解してから3のステップに進むため、`flatMap`の返り値の型は`[Int]`となります。


# 最後に

いかがだったでしょうか？少しでも理解の参考になれば幸いです。

# 参考

* [SequenceType Protocol Reference](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_SequenceType_Protocol/#//apple_ref/swift/intfm/SequenceType/s:FEsPs12SequenceType7flatMapurFzFzWx9Generator7Element_GSqqd___GSaqd___)
* [Optional Enumeration Reference](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_Optional_Enumeration/#//apple_ref/swift/enumm/Optional/s:FSq7flatMapurFzFzxGSqqd___GSqqd___)
* [【Scala】flatMap は怖くない！ ｜ Developers.IO](http://dev.classmethod.jp/server-side/scala-learn-flatmap/)
* [Swift 1.2で導入されたflatMapのユースケース7個を実際のプロジェクトから抜き出してみた - Qiita](http://qiita.com/koher/items/70486a87708c8802985b)