Swiftのmap, filter, reduce（などなど）はこんな時に使う！

# Swiftのmap, filter, reduce（などなど）はこんな時に使う！
Swiftをさわり始めてある程度経つと「Swiftらしく書きたい」という欲望がわいてきます。そしてObjective-Cでは触れたことのない、`map`, `filter`, `reduce`というのものを目にすることになると思います。

これらの関数を見たときの最初の印象は「うわ、何これ」といったもので、見慣れない構文に戸惑いました。同じように戸惑いを感じた方もいるのではないでしょうか。

特に**どんなシチュエーションで、何を使えばよいのか**といった部分があまりピンと来ず、なかなか使いこなせずにいました。

そのような経験を踏まえ、**こういう時は、これを使う！**と一言で説明することを目指してこの記事を書きました。

## はじめに結論から

一言でまとめると、これらの関数を覚えると**めちゃくちゃ便利**になります。

## この関数はこんな時に使う！

個人的にこれから説明する関数を使うようになって感じたメリットをいくつか挙げてみました。

* 配列の操作が圧倒的にやりやすくなる
* 無駄な空配列を用意しなくて済む
* コードの書き方が変わる
* コードが短くなる

ただ、一方でなんでもかんでも`map`や`filter`でやってしまおうとする（書きたくなってしまう）のですが、**Swiftにはほかにも便利な関数が用意されています。**

本文中で説明しますが、例えば`forEach`という関数があるのにもかかわらず`map`を使ってしまったりしていたことがありました。`map`とか`filter`使いたい症候群になっていたのですが、やはり書きやすさや分かりやすさの点で、目的に適した関数を使うことが大事ですよね。

そんな経験を踏まえて、**どんなシチュエーションで何を使えばよいのか**についての指標があれば良いなと思いまとめてみました。

※なお`flatMap`は一言で説明することが難しく、何ができるかの説明にとどまっています。こちらについては良い説明ができるようになったら追記したいと思います。

### `map`は全要素に処理を適用したい時に使う！
`map`は**配列内の要素に処理を適用し、その処理を施した配列**を使いたい場合に使用します。

以下のコードでは、各要素を5倍した新しい配列`newArray`を`map`で生成しています。

```swift
let array = [1,2,3,4,5]
let newArray = array.map { $0 * 5 }
newArray // [5, 10, 15, 20, 25]
```

`map`には`@warn_unused_result`というアトリビュートが指定されており、返り値が使用されていない場合にwarningを出します。戻り値が必要ないけど配列内の要素を使用して何か処理を行いたい場合は後述の`forEach`を使用すると良いでしょう。

`map`は`SequenceType`の準拠した型であれば使用できるので`Dictionary`にも使用できます。以下のコードでは摂氏を華氏に変換しています。

```swift
let celsius = ["Tokyo":14.0, "Osaka":17.0, "Okinawa":26.0]
let fahrenheit = celsius.map { ($0.0, 1.8 * $0.1 + 32) } // [("Okinawa", 78.8), ("Tokyo", 57.2), ("Osaka", 62.6)]
```

全要素に処理をかけたい場合にとても便利な関数です。

### `filter`は条件に合う要素を絞り込む時に使う！
`filter`は、**条件にマッチする要素のみを取り出したい**場合に使用します。

以下のコードでは配列内の3未満の数値を取り出しています。

```swift
let array = [1,2,3,4,5]
let newArray = array.filter { $0 < 3 }
newArray // [1,2]
```

`filter`にも`@warn_unused_result`が指定されています。

以下のコードでは、`map`で摂氏を華氏に変換してから、`filter`で華氏60度以上の地域を絞り込み、`map`で地域名だけを取り出しています。

```swift
let celsius = ["Tokyo":14.0, "Osaka":17.0, "Okinawa":26.0]
let fahrenheit =
celsius.map { ($0.0, 1.8 * $0.1 + 32) } // [("Okinawa", 78.8), ("Tokyo", 57.2), ("Osaka", 62.6)]
    .filter { $0.1 > 60 } //[("Okinawa", 78.8), ("Osaka", 62.6)]
    .map { $0.0 } // ["Okinawa", "Osaka"]
```

### `reduce`は要素をまとめて一つにしたい時に使う！
`reduce`は、**要素使って結果を集計したいような場合**に使用します。

以下のコードでは配列内の要素を全て足し合わせる処理を行っています。**第一引数には初期値となる値**を入れます。

```swift
let array = [1,2,3,4,5]
array.reduce(0) { (num1, num2) -> Int in
    num1 + num2
}
// 15
```

このように書くこともできます。

```swift
let array = [1,2,3,4,5]
array.reduce(0, combine: +) // 15
array.reduce(1, combine: *) // 120
```

以下のコードでは、`map`で摂氏を華氏に変換してから、`reduce`で全地域の温度を足し合わせたものを要素数で割り、華氏での平均気温を算出しています。

```swift
let celsius = ["Tokyo":14.0, "Osaka":17.0, "Okinawa":26.0]
let average = celsius.map { 1.8 * $0.1 + 32 } .reduce(0, combine: +) / Double(celsius.count) // 66.2
```

`reduce`は一見分かりにくく慣れるのに手こずりそうですが、要素をまとめたい場合に非常に便利です。

### `forEach`は`for-in`をより簡潔に記述したい時に使う！
`forEach`は`forin`と同じように使用します。**各要素に対して処理を行い、かつ戻り値が必要ない場合に使用**すると良いと思います。

僕も最初やってしまっていたのですが、各要素に対して処理を行う必要があるが戻り値が必要ない場合でも`map`を使用することは可能です。戻り値の型を`Void`にすれば実現できます。

```swift
array.map { (num) -> Void in
    print("\(num)")
}
```
ただし`map`は`@warn_unused_result`というアトリビュートが付いているので、上記のコードでは**warningが発生**します。

このような場合は`forEach`が適しているでしょう。

```swift
array.forEach { print("\($0)") }
```

### `enumerate`要素と要素のインデックスが欲しい時に使う！
`enumerate `は**インデックスと要素のタプルを返します**。

```swift
let scores = [84, 76, 91, 62 ,80]

for (index, score) in scores.enumerate() {
    print("index:\(index), score: \(score)")
    // index:0, score: 84
    // index:1, score: 76
    // index:2, score: 91
    // index:3, score: 62
    // index:4, score: 80
}
```

`forEach`を使用して、このように書くこともできます。

```swift
let scores = [84, 76, 91, 62 ,80]
scores.enumerate().forEach { print("index:\($0.0), score: \(0.1)") }
```

`indexOf`を組み合わせれば`for-in`や`forEach`でもインデックスを取得することが可能ですが、`enumerate`の方がかなり簡単に処理を書くことが可能です。

### `minElement`or`maxElement`は最小要素or最大要素を取り出したい時に使う！
`minElement`と`maxElement`はそれぞれ**最小要素と最大要素を取り出す**ために使用します。

以下のコードでは、`scores`リストに入っている最小要素と最大要素を取り出しています。

```swift
let scores = [84, 76, 91, 62 ,80]
scores.minElement() // 62
scores.maxElement() // 91
```

`minElement`と`maxElement`を使用しない場合でも以下のように書くことができます。

```swift
let scores = [84, 76, 91, 62 ,80]
scores.sort(<).first // 62
scores.sort(>).first // 91
```

`minElement`、`maxElement`の方がわかりやすいですね。

### `dropFirst`or`dropLast`は先頭要素or最後の要素をカットしたい時に使う！
`dropFirst`は先頭要素をカット、`dropLast`は最後の要素をカットします。

```
let scores = [84, 76, 91, 62 ,80]
scores.dropFirst() // [76, 91, 62, 80]
scores.dropLast()  // [84, 76, 91, 62]
```

なお、順番が保証されていない`Set`や`Dictionary`で使用する場合は意図しないものがカットされる可能性があるので注意しましょう。

## `flatMap`は説明が難しい
`flatMap`に関しては、具体的にこういうケースで使う！という自信を持った説明ができないため、どういうことができるかの説明にとどめます（良い解説方法をひらめいたら更新します）。


以下のコードでは、`array`に`Int`の`Optionnal`型として入っている`Int?`の要素を`flatMap`を使用して`Optional`ではない`Int`に写しています。

```swift
let array: [Int?] = [1, nil, 41, 6, nil, 20, 451, 7]
let flatArray = array.flatMap{ $0 } // [1,41,6,20,451,7]
```

この場合の`flatMap`は`SequenceType`に定義されています。

```swift
extension SequenceType {
    /// Return an `Array` containing the non-nil results of mapping
    /// `transform` over `self`.
    ///
    /// - Complexity: O(*M* + *N*), where *M* is the length of `self`
    ///   and *N* is the length of the result.
    @warn_unused_result
    @rethrows public func flatMap<T>(@noescape transform: (Self.Generator.Element) throws -> T?) rethrows -> [T]
}
```

説明を読むと、`nil`ではない値をマッピングした配列を返す、と書いてあります。これを使えば`Optional`(という文脈)に包まれた型を安全に扱うことができるようになります。

次に以下のコードでは、`array`に`String`の`Optionnal`型として入っている`String?`の要素を1つ目の`flatMap`で`String`に写し、2つ目の`flatMap`で`Int`に写しています。

```swift
let array: [String?] = ["1",nil,"41","abc",nil,"20","451","7"]
let flatArray = array.flatMap{ $0 } // ["1", "41", "abc", "20", "451", "7"]
    .flatMap { Int($0) } // [1, 41, 20, 451, 7]
```

最初の`flatMap`は`nil`を取り除き、2つ目の`flatMap`で各文字を`Int`に変換しています。`"abc"`は当然`Int`にはならないので変換時に`nil`が返ってきますが、`flatMap`は`nil`でない値の配列を返すため含まれていません。

こちらも見てみましょう。この`array`は`Optional`ではありませんが、`Int`の`Array`、`[Int]`が配列に包まれて`[[Int]]`となっています。

```swift
let array: [[Int]] = [[1,41,6],[],[20,451,7]]
let flatArray = array.flatMap { $0 } // [1,41,6,20,451,7]
```

`flatMap`で`[[Int]]`が`[Int]`となっています。この場合の`flatMap`は以下のようになっており`flatMap`の`transform`の戻り値が`SequenceType`の場合に、こちらになります。

```swift
extension SequenceType {
    /// Return an `Array` containing the concatenated results of mapping
    /// `transform` over `self`.
    ///
    ///     s.flatMap(transform)
    ///
    /// is equivalent to
    ///
    ///     Array(s.map(transform).flatten())
    ///
    /// - Complexity: O(*M* + *N*), where *M* is the length of `self`
    ///   and *N* is the length of the result.
    @warn_unused_result
    @rethrows public func flatMap<S : SequenceType>(transform: (Self.Generator.Element) throws -> S) rethrows -> [S.Generator.Element]
}
```

説明にもある通りこのコードは以下のように書くこともできます。

```swift
let array: [[Int]] = [[1,41,6],[],[20,451,7]]
let flatArray = array.map {$0} .flatten() // [1,41,6,20,451,7]
```

`flatten`は`self`の要素をつなぐ関数で以下のように定義されています

```swift
extension CollectionType where Generator.Element : CollectionType, Index : BidirectionalIndexType, Generator.Element.Index : BidirectionalIndexType {
    /// A concatenation of the elements of `self`.
    @warn_unused_result
    public func flatten() -> FlattenBidirectionalCollection<Self>
}
```

では、`[[Int?]]`なリストも見てみましょう。

```swift
let array: [[Int?]] = [[1,nil,6],[nil, nil],[20,451,7]]
let flatArray = array.flatMap {$0} // [Optional(1),nil,,Optional(6),nil,nil,Optional(20),Optional(451),Optional(7)]
    .flatMap {$0} // [1,6,20,451,7]
```
まずは`map`+`flatten`と同じ結果となる`flatMap`が呼ばれ、`[[Int?]]`が`[Int]`となります。2つ目の`nil`の要素を含まないリストを返す`flatMap`で`Optional`でない`Int`に値が写されています。



`flatMap`の参考にした記事を以下にまとめましたので、こちらもご参照ください。

#### `flatMap`についての参考記事
* [[Swift][Objective-C] 同期的な関数ハンドラを示す noescape ディレクティブ](http://dev.classmethod.jp/smartphone/noescape-directive/)
* [mapとflatMapという便利メソッドを理解する](http://qiita.com/shimesaba/items/1a89cb5600454f91cc67)
* [GeneratorとSequence](http://qiita.com/naoty_k/items/970796fe56b4ab083278)
* [【Swift】配列の配列について詳しく。](http://qiita.com/ken0nek/items/dce8281570a2dcf48e17)
* * [モナドについてSwiftで説明してみた](http://qiita.com/koher/items/12f72f2fc2db7fa709cf)
* [【Scala】flatMap は怖くない！](http://dev.classmethod.jp/server-side/scala-learn-flatmap/)

## 具体例
ここでは、ここまでに紹介した関数を使用した具体例を紹介します。

### 具体例1: iOSで使用可能なフォントをすべて出力する
iOSのバージョンが上がるたびに使用可能なフォントを確認するためにフォント名の出力を行っていたと思いますが、これはこのように書くことができます。

```swift
UIFont.familyNames().map {UIFont.fontNamesForFamilyName($0)} .flatten() .sort() .forEach { print("\($0)") }
```

まず`UIFont.familyNames()`でフォントファミリー名の配列を取得し、そのフォントファミリー名の配列に対して`map`を使用します。`map`の結果から得られるのはフォントファミリー名の配列（=`[String]`）です。

`map`で適用する関数として`fontNamesForFamilyName`を使用し、フォントファミリーに属するフォント名の配列を取得します。

```swift
UIFont.familyNames().map {UIFont.fontNamesForFamilyName($0)}
```

上記処理までに取得できるものは、" "フォントファミリーに属するフォント名の配列" を格納した配列"（=`[[String]]`）となっています。

これを`flatten`を使用して`[[String]]`から`[String]`に変換し、最後にソートしています。

ここまでの説明で処理の流れがつかめないという方は、Playgroundを使用して各関数ごとに区切って`print`で出力しながら確認してみると理解が進むと思います。

## まとめ
Functionalに書こうと思い始めた時に、`map`や`filter`や`reduce`を使い始めたのですが、配列などの要素に対する操作がObjective-Cに比べてすごく扱いやすくなったと感じています。

一方で`map`や`filter`、その他の関数を組み合わせれば実現可能ではあるものの、「なんだか`map`と`filter`を使いたくて無駄なことをしているな」と感じたことが結構ありました。例えば`forEach`という関数があるのにもかかわらず`map`を使ってしまったりしていたことがありました。

冒頭でも述べましたが、**どんなシチュエーションで何を使えばよいのかについての、おおざっぱでも良いので、指標があれば良いな**と思いまとめてみました。

ぜひ使い方を覚えて楽しいSwiftコーディングライフを送りましょう。

## 参考
* [SequenceType Protocol Reference](https://developer.apple.com/library/prerelease/ios/documentation/Swift/Reference/Swift_SequenceType_Protocol/index.html#//apple_ref/swift/intf/s:PSs12SequenceType)
* [CollectionType Protocol Reference](https://developer.apple.com/library/ios/documentation/Swift/Reference/Swift_CollectionType_Protocol/index.html#//apple_ref/swift/intf/s:PSs14CollectionType)