Swiftでコレクション(配列など)を扱う際、データの変換とフィルタリングを同時に行いたい場面は多くあります。そんなときに便利なのがcompactMapというメソッドです。
この記事では、Swift初心者の方に向けて、compactMapの基本から実践的な使い方まで、わかりやすく解説します。
compactMapとは?
compactMapは、Swiftの配列やその他のコレクション型で使用できるメソッドで、次の2つの処理を同時に行います。
- 各要素を変換する(マッピング)
- 結果がnilの要素を自動的に除外する(フィルタリング)
簡単に言えば、「変換してnilを取り除く」メソッドです。
compactMapの基本的な使い方
文字列を数値に変換する例
最もわかりやすい例として、文字列の配列を数値の配列に変換してみましょう。
let stringNumbers = ["1", "2", "three", "4", "five"]
let numbers = stringNumbers.compactMap { Int($0) }
print(numbers)
// 出力: [1, 2, 4]
何が起きているのか?
- “1” →
Int("1")
→ 1に変換成功 - “2” →
Int("2")
→ 2に変換成功 - “three” →
Int("three")
→ 変換失敗でnil - “4” →
Int("4")
→ 4に変換成功 - “five” →
Int("five")
→ 変換失敗でnil
compactMapは変換に失敗してnilになった要素を自動的に除外してくれるため、結果は[1, 2, 4]
となります。
mapとの違いを理解する
compactMapを理解するには、通常のmap
との違いを知ることが重要です。
mapを使った場合
let stringNumbers = ["1", "2", "three", "4"]
let result = stringNumbers.map { Int($0) }
print(result)
// 出力: [Optional(1), Optional(2), nil, Optional(4)]
mapは全ての要素を変換しますが、nilもそのまま残ります。結果は[Int?]
型(オプショナルの配列)になります。
compactMapを使った場合
let stringNumbers = ["1", "2", "three", "4"]
let result = stringNumbers.compactMap { Int($0) }
print(result)
// 出力: [1, 2, 4]
compactMapはnilを自動的に除外し、結果は[Int]
型(非オプショナルの配列)になります。
比較表
メソッド | nil要素の扱い | 結果の型 | 用途 |
---|---|---|---|
map | nilも含まれる | [Int?] | 全要素を変換したい |
compactMap | nilは除外される | [Int] | nilを除外して変換したい |
compactMapの実践的な使い方
1. オプショナル配列から値を取り出す
let optionalNumbers: [Int?] = [1, nil, 3, nil, 5, 6]
let validNumbers = optionalNumbers.compactMap { $0 }
print(validNumbers)
// 出力: [1, 3, 5, 6]
これは最もシンプルな使い方で、オプショナル配列から非nilの値だけを取り出します。
2. 型変換とフィルタリングを同時に行う
let mixedData = ["apple", "100", "banana", "200", "cherry", "300"]
let prices = mixedData.compactMap { Int($0) }
print(prices)
// 出力: [100, 200, 300]
文字列と数値が混在したデータから、数値だけを抽出できます。
3. URLの配列を作成する
let urlStrings = [
"https://example.com",
"not a url",
"https://apple.com",
"invalid url format"
]
let validURLs = urlStrings.compactMap { URL(string: $0) }
print(validURLs.count)
// 出力: 2(有効なURLは2つ)
無効なURL文字列を自動的に除外して、有効なURL配列を作成できます。
4. ネストした配列の処理
let nestedArrays: [[Int]?] = [[1, 2], nil, [3, 4], nil, [5]]
let flattenedArrays = nestedArrays.compactMap { $0 }
print(flattenedArrays)
// 出力: [[1, 2], [3, 4], [5]]
5. カスタム変換ロジック
struct User {
let name: String
let age: Int
}
let userData = [
["name": "太郎", "age": "25"],
["name": "花子", "age": "invalid"],
["name": "次郎", "age": "30"]
]
let users = userData.compactMap { dict -> User? in
guard let name = dict["name"],
let ageString = dict["age"],
let age = Int(ageString) else {
return nil
}
return User(name: name, age: age)
}
print(users.count)
// 出力: 2(有効なユーザーは2人)
辞書データから構造体を作成する際、無効なデータを自動的にスキップできます。
compactMapを使うべき場面
compactMapは以下のような場面で特に有効です。
- 型変換が失敗する可能性がある場合
- 文字列から数値への変換
- 文字列からURLやDateへの変換
- オプショナル配列から非nilの値だけが欲しい場合
- データベースやAPIから取得したオプショナルデータの処理
- 無効なデータをスキップしたい場合
- ユーザー入力の検証とフィルタリング
- ファイルパースでの不正データの除外
よくある間違いと注意点
1. flatMapとの混同
Swift 4.1以前では、compactMapの機能はflatMap
という名前でした。現在のSwiftでは、flatMapは別の用途(ネストした配列の平坦化)に使われるため、混同しないよう注意しましょう。
// 古いコード(非推奨)
let result = array.flatMap { Int($0) }
// 新しいコード
let result = array.compactMap { Int($0) }
2. パフォーマンスの考慮
compactMapは便利ですが、大量のデータを処理する場合はパフォーマンスに注意が必要です。必要に応じて、filter
とmap
を分けて使うことも検討しましょう。
// compactMapを使う場合
let result1 = array.compactMap { transform($0) }
// filterとmapを分ける場合
let result2 = array.filter { isValid($0) }.map { transform($0) }
まとめ
compactMapは、Swiftでデータ処理を行う際に非常に便利なメソッドです。
ポイントのおさらい:
- compactMapは「変換」と「nilの除外」を同時に行う
- mapとの違いは、nilを自動的に除外すること
- 型変換が失敗する可能性がある場合に特に有効
- オプショナル配列から非nilの値を取り出すのに便利
初心者の方は、まずシンプルな例から試してみて、徐々に複雑なケースにも挑戦してみてください。compactMapをマスターすれば、よりシンプルで読みやすいSwiftコードが書けるようになります。