Swiftにおける配列操作関数


配列操作関数とは、その名の通り配列を操作するための関数である。

## forEach
よくあるfor文を、配列操作関数forEachに置き換えた場合

```swift
let arr1 = [1, 2, 3, 4, 5]

do {
    for v in arr1 {
        print(v)
    }
}
```

```swift
do {
    arr1.forEach { (v) in
    print(v)
    }
}
```
```console
1
2
3
4
5
```

中身がクロージャになっているので、好きな関数を渡すことができる。

ドキュメントには、
> Calls the given closure on each element in the sequence in the same order as a for-in loop.
(for-inループと同じ順序で、シーケンス内の各要素に対して与えられたクロージャを呼び出します。)

とある。

## filter

filterは、中身のクロージャがtrueの時だけ抜き出すというもの。

```swift
do {
    let arr2 = arr1.filter { (v) -> Bool in
        v % 2 == 0
    }
    
    for v in arr2 {
        print(v)
    }
}
```
```console
2
4
```
偶数かどうかの評価がtrueだった時に、arr2に追加される。

以下ドキュメント
> Returns an array containing, in order, the elements of the sequence that satisfy the given predicate.
(与えられた式の結果を満たすときに、配列の中身を返します。)

上記の内容は、メソッドを繋げて表現することも可能。

```swift
do {
    arr1.filter { (v) -> Bool in
        v % 2 == 0
    }.forEach { (v) in
        print(v)
    }
}
```
```console
2
4
```
filterを通してからforEachで回した。

## map

mapは、要素ひとつずつに処理をすることができる。
下記のように、ひとつずつの型を変えることが可能である。(最も使用頻度が高い)

```swift
do {
    let arr2 = arr1.map { (v) -> String in
        String(v) + "です"
    }
    
    for v in arr2 {
        print(v)
    }
}
```
```console
1です
2です
3です
4です
5です
```
上の例は、戻り値をStringにした場合で、
配列の要素一つずつをStringに変える、という意味である。

以下ドキュメント
> Returns an array containing the results of mapping the given closure over the sequence’s elements.
(指定したクロージャをシーケンスの要素にマッピングした結果を含む配列を返します。)

## reduce

reduceは、要素を用いて結果をまとめたいときなどに用いる。

```swift
do {
    let v = arr1.reduce("") { (result, v) -> String in
        result + "\(v)です, "
    }
    print(v)
}
```
```console
1です, 2です, 3です, 4です, 5です, 
```
まずは初期値を自由に設定できる。今回は、""(空文字)を渡している。
resultはカラ文字、vには配列の要素が入る。

返り値はStringなので、String型の結果を式の中で構築する。

最初は、"空文字 + 1です, "の String が返る。
2周目は、引数のresultの中には直前に返された "1です, " が入っており、 vにはarr1の次の要素である2が来る。

これが繰り返されることで、一つのresult が更新されていく。

以下ドキュメント
> Returns the result of combining the elements of the sequence using the given closure.
(指定したクロージャを用いてシーケンスの要素を結合した結果を返します。)

## 実践的な例

id と name プロパティを持つユーザーモデルを例に、より実践的な例をあげる。

以下は、filterで条件を絞り(この場合、偶数だけに絞る)、 mapでユーザーモデルを構築している。

```swift
struct User {
    var id: Int = 0
    var name: String = ""
}

do {
    let users = arr1.filter { (v) -> Bool in
        v % 2 == 0
    }.map { (v) -> User in
        User(id: v, name: "偶数\(v)です")
    }
    
    for user in users {
        print("id: \(user.id), name:\(user.name)")
    }
}
```
```console
id: 2, name:偶数2です
id: 4, name:偶数4です
```
配列からユーザーモデルを生成できた。
検索の絞り込みなどをアプリ側で行いたいときなどは、こういった機能が必要になってくる。

## 省略記法
```swift

do {
    arr1.filter { $0 % 2 == 0 }.forEach { print($0) }
    
    arr1
        .filter { $0 % 2 != 0 }
        .map { User(id: $0, name: "奇数\($0)です") }
        .forEach {
            print("id: \($0.id), name: \($0.name)")
    }
}
```
```console
2
4
id: 1, name: 奇数1です
id: 3, name: 奇数3です
id: 5, name: 奇数5です
```
$0は、クロージャ式でいうところの、パラメータ v を表している。
1行しかない場合、それが return を意味することになる。

一方で、下記のように２行以上にした場合、return にならないので、エラーとなる。

```swift
do {
    arr1.filter {
        print("ああ")
        $0 % 2 == 0 }.forEach { print($0) } // ここでエラー発生. Missing return in a closure expected to return 'Bool'; did you mean to return the last expression?
}
```

Boolを返せと怒られている。

printでデバックした時などは、このエラーが出るので注意が必要である。