コレクションを賢く扱うために知るべきメソッド
Collectionをスマートに使うためのmap、filter、reduce
はじめに
Swiftの配列(コレクション)にはシーケンスプロトコルが用意されています。
シーケンスとは、一方向からの順次アクセス可能なデータ構造のことを指します。
配列は先頭のインデックスから要素に順次アクセスできるため、シーケンスの一種です。
つまり、このような配列に標準的に備わったメソッド定義がシーケンスプロトコルです。
シーケンスプロトコルには以下のようなインタフェースがあります。
- forEach(_:)
- filter(_:)
- map(_:)
- flatMap(_:)
- compactMap(_:)
- reduce(_:_:)
ここではよく使われるmap、filter、reduceの3つを紹介します。
使用方法
mapメソッドは、すべての要素を特定の処理を用いて変換する場合に使用します。
次の2つのコードはどちらも同じ処理で結果も同じです。
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var mappedNumbers = [Int]()
for num in numbers {
let doubleNum = num * 2
mappedNumbers.append(doubleNum)
}
mappedNumbers // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let mappedNumbers = numbers.map { $0 * 2 }
mappedNumbers // [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
[Int]型の配列の各要素を2倍にした、配列mappedNumbersを返しています。後者の方が、事前のmappedNumbersの宣言とfor文を省略でき、コードがスッキリすることが分かります。
ちなみに、以下の要領で[Int]型の配列を[String]型の配列に変換することも可能です。
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let stringNumbers = numbers.map { String($0) }
stringNumbers // ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]
filterメソッドは、フィルターという文字どおり、指定した条件を満たす要素のみに絞り込みたい場合に使用します。
次の2つのコードはどちらも同じ処理で結果も同じです
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var filteredNumbers = [Int]()
for num in numbers {
if num % 2 == 0 {
filteredNumbers.append(num)
}
}
filteredNumbers // [2, 4, 6, 8, 10]
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let filteredNumbers = numbers.filter { $0 % 2 == 0 }
filteredNumbers // [2, 4, 6, 8, 10]
[Int]型の配列の各要素のうち2で割り切れる値(2の倍数)のみに絞り込み、配列filteredNumbersを返しています。後者の方が、事前のfilteredNumbersの宣言とfor文を省略でき、コードがスッキリすることが分かります。
引数のクロージャはBool型の戻り値を返し、戻り値がtrueなら要素は新しいシーケンスに含まれ、falseなら含まれない作りになっています。
reduceメソッドは、すべての要素を1つの値にまとめます。
次の2つのコードはどちらも同じ処理で結果も同じです
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
var sum = 0
for num in numbers {
sum += num
}
sum // 55
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let sum = numbers.reduce(0) { $0 + $1 }
sum // 55
第1引数に初期値を指定し、第2引数に要素を結果に反映する処理を指定します。
上記の例では初期値が0で、クロージャ内がfor文で繰り返されることをイメージすると、$0に常にそれまで足し合わされた合計値が入り、$1に次の要素値が入るイメージです。
後者の方が、事前のsumの宣言とfor文を省略でき、コードがスッキリすることが分かります。
ちなみに、以下の要領で[Int]型の配列を[String]型の配列に変換することも可能です。
let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9]
let concatNumbers = numbers.reduce("") { $0 + String($1) }
concatNumbers // "123456789"