【iOS】配列を効率よく使いこなすmap、filter、reduceなどのシーケンスプロトコル
notwork_ios_collection
スポンサーリンク

コレクションを賢く扱うために知るべきメソッド

Collectionをスマートに使うためのmap、filter、reduce

ヒーラー
配列の中を操作する場合は、大抵for文を書いて処理するが、自明なことは省略できるならしたい

はじめに

Swiftの配列(コレクション)にはシーケンスプロトコルが用意されています。
シーケンスとは、一方向からの順次アクセス可能なデータ構造のことを指します。
配列は先頭のインデックスから要素に順次アクセスできるため、シーケンスの一種です。
つまり、このような配列に標準的に備わったメソッド定義がシーケンスプロトコルです。

シーケンスプロトコルには以下のようなインタフェースがあります。

  • forEach(_:)
  • filter(_:)
  • map(_:)
  • flatMap(_:)
  • compactMap(_:)
  • reduce(_:_:)

ここではよく使われるmap、filter、reduceの3つを紹介します。

使用方法

map -配列の要素を変換する-

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 -要素を絞り込む-

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つの値にまとめる-

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"

スポンサーリンク

Twitterでフォローしよう

おすすめの記事