mapとflatMapという便利メソッドを理解する

最近になってmapやflatMapの勉強をしたのでその辺りを書いてみます。

mapやflatMapなんですが配列にもOptionalにもあるしObjective-cにはないメソッドなので最初見た時は少し混乱しました。
それぞれ別物と捉えて1つ1つについて調べていったら分かりやすかったので、その視点で記事を書いていきます。

今回はmapは2種類、flatMapは3種類に分類しました。

## map(配列用)
1つ目のmapは配列のメソッドです。(正確にはSequenceTypeやらCollectionTypeにあるメソッドなので配列以外でも使えますが)
配列のmapは配列の要素1つ1つに操作をした結果の配列を返します。

```swift
[1, 2, 3].map { $0 * 2 } // → [2, 4, 6]

["3", "2", "1"].map { Int($0) } // → [Optional(3), Optional(2), Optional(1)]
```

クラスの配列からID一覧を取得したりする時に便利でした。

```swift
class MyClass {
    var id: Int
    init(id: Int) {
        self.id = id
    }
}
let myClasses = [MyClass(id: 1), MyClass(id: 2), MyClass(id: 3)]
myClasses.map { $0.id } // → [1, 2, 3]
```

## map(Optional用)
mapメソッドはOptionalにもあります。
中身がnilなら何もせず、中身がnil以外なら処理をするものです。

```swift
let value: Int? = 1
var result: Int? = value.map { $0 + 1 } // → Optional(2)
// ↑ と ↓ は同じ意味
result = value != nil ? value! + 1 : nil // → Optional(2)
```

便利ではあるのですが、自分は`if let`や`guard`でnilを除外する事が多いので配列のmapに比べると利用頻度は低いです。

```swift
let optionalValue: Int? = 1
guard value = optionalValue else { return }

result = value + 1 // → 1
```

## flatMap(2重の配列用)
次にflatMapについて書きます。
flatMapはmapのような動きをして、更に2重の配列を1重に直してくれます。

```swift
[[1, 2], [3]].flatMap { $0 } // → [1, 2, 3]
        
[[1, 2], [3]].flatMap { $0 + [1] } // → [1, 2, 1, 3, 1]
```

mapで同じ事をすると下の結果になります。

```swift
[[1, 2], [3]].map { $0 } // → [[1, 2], [3]]
        
[[1, 2], [3]].map { $0 + [1] } // → [[1, 2, 1], [3, 1]]
```

## flatMap(Optional用)
flatMapメソッドはOptional型にも定義されています。
配列のflatMapはmapをした上で2重の配列を1重にしてくれましたが、OptionalのflatMapはmapをした上で結果をアンラップします。

```swift
let value: String? = "1"
// mapだと2重のOptionalになってしまう
value.map { Int($0) } // Optional(Optional(1))

// flatMapならただのOptionalになる
value.flatMap { Int($0) } // Optional(1)
```

ブロックの結果がnilの時はnilを返します。

```swift
let value: String? = ""
print(value.map { Int($0) }) // Optional(nil)

print(value.flatMap { Int($0) }) // nil
```

クロージャーがOptionalを返さない場合はどちらも同じ結果になります。

```swift
var value: Int? = nil
value.flatMap { $0 + 1 } // → nil
value = 1
value.flatMap { $0 + 1 } // → Optional(2)

value = nil
value.map { $0 + 1 } // → nil
value = 1
value.map { $0 + 1 } // → Optional(2)
```

## flatMap(中がオプショナルな配列用)
最後にSwift2.0で追加されたflatMapです。
中身がOptionalの配列を中身をアンラップした配列にしてくれます。

```swift
["1", "2", "3"].map { Int($0) } // → [Optiona(1), Optiona(2), Optiona(3)]
["1", "2", "3"].flatMap { Int($0) } // → [1, 2, 3]
```

nilがある場合はnilを除外してくれます。

```swift
["1", "2", "string", "3"].map { Int($0) } // → [Optiona(1), Optiona(2), nil, Optiona(3)]
["1", "2", "string", "3"].flatMap { Int($0) } // → [1, 2, 3]
```

配列からnilを取り除く時も便利です。

```swift
[1, 2, nil, 3].flatMap { $0 } // → [1, 2, 3]
```

## 2重で中身がOptionalな配列にflatMapを使うとどうなるか
先ほどflatMapは2重の配列を1重の配列に直すものと中身のOptionalを取り出す2種類があると書きました。

```swift
[1, 2, 3, nil].flatMap { $0 } // → [1, 2, 3]
[[1], [2, 3]].flatMap { $0 } // → [1, 2, 3]
```

2重で中身がOptionalな配列にflatMapを使ったらどうなるか試してみました。

結果は下の通りで、Optionalをアンラップする方が優先されました。

```swift
[[1], [2, 3], nil].flatMap { $0 } // → [[1], [2, 3]]
```

もう一度flatMapを使えば2重の配列の中身を取り出せました。

```swift
[[1], [2, 3], nil].flatMap { $0 }.flatMap { $0 } // → [1, 2, 3]
```

## mapとflatMapが同じ結果になるのはなぜか
mapとflatMapは場合によっては同じ値を返します。
なぜ同じ結果になるかを調べてみました。

```swift
[1, 2, 3].flatMap { $0 + 1 } // → [2, 3, 4]
[1, 2, 3].map { $0 + 1 } // → [2, 3, 4]
```

下の式を見ると分かるのですが、flatMapの戻り値がInt?になっています。
つまり下のようなケースでは中身がオプショナルな配列用のflatMapが使われます。
そして戻り値は通常のInt型がOptionalに変換されたものが返り、それがアンラップされるからmapと同様の結果になるようです。

```swift
[1, 2, 3].flatMap { (value: Int) -> Int? in
    return value + 1
}
[1, 2, 3].map { (value: Int) -> Int in
    return value + 1
}
```

## 参考URL
[Swift 2.0の新しいflatMapが便利過ぎる](http://qiita.com/koher/items/6a95cb8edb04fb81f787)