イメージで理解するSwiftの高階関数（filter, map, reduce, compactMap, flatMap）

# はじめに
本記事ではSwiftの初心者〜中級者向けに[Swift Standard Library](https://developer.apple.com/documentation/swift/swift_standard_library)フレームワークで提供されている主な**高階関数**について説明します。以下のようにイメージを合わせて書くことで、イメージが掴みやすいようにしました。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/74114bbf-c76f-116c-7b7f-318867561e04.png" width="600px">

高階関数とは、**関数を引数や戻り値にとる関数**のことです。Swift Standard Libraryでは主に配列などで利用でき、for文で配列の各要素を処理するよりも**宣言的なプロラミング**が可能です。

**例題）1〜10の数字から2の倍数だけ取得して、それぞれ3倍したものの合計**

```swift
// for文を用いた例（命令型プログラミング）
let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var result = 0
for element in array {
    guard element % 2 == 0 else { continue }
    result = result + (element * 3)
}

// 高階関数を用いた例（宣言型プログラミング）
// より例題をそのまま表現している
let array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let result = array
    .filter { $0 % 2 == 0 }
    .map { $0 * 3 }
    .reduce(0) { $0 + $1 }
```

具体的な処理を関数（クロージャー）として指定できるため、高階関数自体が汎用的で利用しやすいものとなっています。

## 本記事で説明する高階関数

大きくはSequenceプロトコルに準拠した型（Arrayなど）で利用できるものとOptional型で利用できるものを説明します。
map、flatMapについては以下の通り、型によって2種類の関数があります。

 - Sequence
   - [filter](https://developer.apple.com/documentation/swift/sequence/3018365-filter)
   - [map](https://developer.apple.com/documentation/swift/sequence/3018373-map)
   - [reduce](https://developer.apple.com/documentation/swift/sequence/2907677-reduce)
   - [compactMap](https://developer.apple.com/documentation/swift/sequence/2950916-compactmap
)
   - [flatMap](https://developer.apple.com/documentation/swift/optional/1540500-flatmap)

 - Optional
   - [map](https://developer.apple.com/documentation/swift/optional/1539476-map)
   - [flatMap](https://developer.apple.com/documentation/swift/optional/1540500-flatmap)

# Sequenceの高階関数

## [filter](https://developer.apple.com/documentation/swift/sequence/3018365-filter)

### イメージ

「リストの要素からなにかの条件に合うものだけを返す」

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/74114bbf-c76f-116c-7b7f-318867561e04.png" width="600px">

### 利用例

```swift
// 🥬と🐛のリストから、🐛を取り除いたリストを取得する
let array = ["🥬", "🐛", "🐛", "🥬", "🥬", "🐛"]

// このとき、$0はリスト内のそれぞれの要素を意味する
let result = array.filter { $0 != "🐛" }

print(result) // ["🥬", "🥬", "🥬"]
```

## [map](https://developer.apple.com/documentation/swift/sequence/3018373-map)

### イメージ

「リストの全ての要素に何か変換をして返す」

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/c9e8a6a8-aba0-2796-6193-db914f2cde60.png" width="600px">

### 利用例

```swift
// 🥬のリストの要素を🥗に変換したリストを取得する
let array = ["🥬", "🥬", "🥬"]

// ここで利用したreplacingOccurrences関数は文字列を置き換える関数
let result = array.map { $0.replacingOccurrences(of: "🥬", with: "🥗") }

print(result) // ["🥗", "🥗", "🥗"]
```

## [reduce](https://developer.apple.com/documentation/swift/sequence/2907677-reduce)

### イメージ

「全ての要素に何か変換をして1つの結果を得る」

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/1e98579e-cba0-ef2c-56f5-11c34425dd0d.png" width="600px">

### 利用例

```swift
// 複数の🍓を含むリストから合計のいちごの数を数える
let array = ["🍓🍓", "🍓🍓🍓", "🍓"]

// このとき、$0は累積の結果、$1はリスト内のそれぞれの要素を意味する
// 第1引数の0は累積していく値の初期値
let result = array.reduce(0) { $0 + $1.count }

print(result) // 6
```

## [compactMap](https://developer.apple.com/documentation/swift/sequence/2950916-compactmap)

### イメージ

「全ての要素に何か変換をして、nilになったものを取り除いて返す」

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/7213ba66-6625-e953-b05a-b8ca60c0e6bf.png" width="600px">

### 利用例

```swift
// 🥬の場合は🥗に変換し、それ以外の場合はnilを返す
// nilとなる要素はcompactMapで取り除く
let array = ["🥬", "⚽", "🥬"]

let result = array.compactMap { $0 == "🥬" ? "🥗" : nil }
print(result) // ["🥗", "🥗"]
```

**※補足**
上記と同様の処理をmapを利用して行った場合は、nilを含む`Optional<String>`型の配列となります。

```swift
let array = ["🥬", "🥦", "🥬"]
let result = array.map { $0 == "🥬" ? "🥗" : nil }
print(result) // ["🥗", "🥗"] // [Optional("🥗"), nil, Optional("🥗")]"
```

## [flatMap](https://developer.apple.com/documentation/swift/sequence/2905332-flatmap)

### イメージ

「全ての要素に何か変換をして、その結果の要素が配列であればそれをフラットにして返す」

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/0094b009-964d-c198-7c02-a8ba47a65278.png" width="600px">

### 利用例

```swift
// 要素の🧟を[🧟, 🧟, 🧟]に変換し、その多重配列をフラットにしたものを返す
let array = ["🧟", "🧟"]
let result = array.flatMap { Array(repeating: $0, count: 3) }
print(result) // ["🧟", "🧟", "🧟", "🧟", "🧟", "🧟"]
```

**※補足**
上記と同様の処理をmapを利用して行った場合は、`[[String]]`型の配列となります。

```swift
let array = ["🧟", "🧟"]
let result = array.map { Array(repeating: $0, count: 3) }
print(result) // [["🧟", "🧟", "🧟"], ["🧟", "🧟", "🧟"]]
```

# Optionalの高階関数

## [map](https://developer.apple.com/documentation/swift/optional/1539476-map)

### イメージ

「Optional型でラップされた型（中身）に対して何か変換を行う」

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/dee8a2da-0054-6f19-cfb9-671254904632.png" width="600px">

### 利用例

```swift
// Optional<String>型の変数をInt型に変換する
// init?(_ description: String)は失敗可能イニシャライザであるため、
// その結果はさらにOptional型となる
let number: String? = "01"
print(number.map { Int($0) })
```

## [flatMap](https://developer.apple.com/documentation/swift/optional/1540500-flatmap)

### イメージ

「Optional型でラップされた型（中身）に対して何か変換を行い、その結果がOptional型であればアンラップする（多重のOptionalをフラットにする）」

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113553/91c3d3f3-354d-a979-abd4-24a06758f546.png" width="600px">

### 利用例

```swift
// Optional<String>型の変数をInt型に変換する
// init?(_ description: String)は失敗可能イニシャライザであるため、
// その結果はさらにOptional型となるがflatMapによりアンラップされる
let number: String? = "01"
print(number.flatMap { Int($0) })
```

※失敗可能イニシャライザがわからない場合はこちらの記事で解説をしていますのでご覧ください。

[Swiftとイニシャライザ](https://qiita.com/shtnkgm/items/8b7979fc84a3cc065238)

# その他の高階関数

Sequenceの高階関数の一覧です。Optionalは先述のmapとflatMapのみです。クリックすると公式ドキュメントに飛びます。

 - [`contains(where:)`](https://developer.apple.com/documentation/swift/sequence/2905153-contains)
 - [`first(where:)`](https://developer.apple.com/documentation/swift/sequence/2907979-first)
 - [`min(by:)`](https://developer.apple.com/documentation/swift/sequence/2907979-first)
 - [`max(by:)`](https://developer.apple.com/documentation/swift/sequence/2906531-max)
 - [`sorted(by:)`](https://developer.apple.com/documentation/swift/sequence/2907222-sorted)
 - [`elementsEqual(_:by:)`](https://developer.apple.com/documentation/swift/sequence/2949668-elementsequal)
 - [`lexicographicallyPrecedes(_:by:)`](https://developer.apple.com/documentation/swift/sequence/2907758-lexicographicallyprecedes)
 - [`drop(while:)`](https://developer.apple.com/documentation/swift/sequence/3128801-drop)
 - [`forEach(_:)`](https://developer.apple.com/documentation/swift/sequence/3018369-foreach)
 - [`prefix(while:)`](https://developer.apple.com/documentation/swift/sequence/3128810-prefix)
 - [`split(maxSplits:omittingEmptySubsequences:whereSeparator:)`](https://developer.apple.com/documentation/swift/sequence/3128814-split)
 - [`starts(with:by:)`](https://developer.apple.com/documentation/swift/sequence/2996828-starts)
 - [`withContiguousStorageIfAvailable(_:)`](https://developer.apple.com/documentation/swift/sequence/3128824-withcontiguousstorageifavailable)

# 補足: 高階関数の構文について

上記の高階関数の説明では、Swiftの後置クロージャー（trailing closure）と簡易引数名（shorthand argument name）を利用しています。

**後置クロージャー（trailing closure）**は関数の最後の引数がクロージャーの場合に以下のように最後の引数ラベルを省略し、クロージャーを関数の後に置いて記述できるという文法です。例えばUIViewのanimate関数では、以下のように2つの書き方ができます。

```swift
// UIViewのanimate関数
class func animate(withDuration duration: TimeInterval, animations: @escaping () -> Void)
```

```swift
// 後置クロージャーを利用しない場合
UIView.animate(withDuration: 2.0, animations: { label.alpha = 0 })

// 後置クロージャーを利用する場合
UIView.animate(withDuration: 2.0) { label.alpha = 0 }
```

また、クロージャーが引数を取る場合に、その引数名を省略できるという**簡易引数名（shorthand argument name）**の構文もあります。引数名を省略する場合は順に`$0`, `$1`, `$2`...という名前で参照できます。

```swift
// 簡易引数名を利用しない場合
let numbers = [1, 2, 3, 4, 5]
let result = numbers.filter { number in number % 2 == 0 }

// 簡易引数名を利用する場合
let numbers = [1, 2, 3, 4, 5]
let result = numbers.filter { $0 % 2 == 0 }
```

# 参考

 - [[Swift]flatMap・compactMapの挙動はソースコードを読んで理解しよう〜型情報と実装から難関メソッドを突破する〜](https://qiita.com/crea/items/b065425bde990cbd1c82)
 - [Sequence - Apple Swift Standard Library](https://developer.apple.com/documentation/swift/sequence)
 - [Optional - Apple Swift Standard Library](https://developer.apple.com/documentation/swift/optional)