はじめに
以下の構造体があります。
struct Manga {
let title: String
let volume: Int
var selected: Bool
}
こちらの構造体を格納した配列があり、全要素の selected
プロパティを false
にするにはどうすればいいでしょうか?
var mangas: [Manga] = [
Manga(title: "進撃の巨人", volume: 34, selected: false),
Manga(title: "ワンパンマン", volume: 23, selected: true),
Manga(title: "プラチナエンド", volume: 14, selected: true)
]
func deselectMangas() {
// TODO: `mangas` 内の漫画をすべて選択解除する
}
ぱっと思いつくのは forEach()
メソッドで全要素をループし、 selected
プロパティに false
を代入する方法です。
func deselectMangas() {
mangas.forEach { $0.selected = false } // FIXME: Build error
}
残念ながら Cannot assign to property: '$0' is immutable
エラーが発生してビルドできません。
forEach()
では要素が不変のため、変更しようとするとビルドエラーになります。
解決する方法はいくつかあり、代表して3つほど紹介します。
環境
- OS:macOS Big Sur 11.1
- Xcode:12.4 (12D4e)
- Swift:5.3.2
配列の要素をまとめて更新する方法
①配列をインデックスでアクセスする
配列をインデックスでアクセスすると要素が可変のため、プロパティを変更できます。
数字を配列の要素数だけループすることでスッキリ書けます。
func deselectMangas() {
(0..<mangas.count).forEach { mangas[$0].selected = false }
}
indices
プロパティを使うとよりスマートかもしれません。
func deselectMangas() {
mangas.indices.forEach { mangas[$0].selected = false }
}
②配列を再生成する
配列の要素を map()
などのメソッドで変換し、配列を再生成します。
func deselectMangas() {
mangas = mangas.map {
var manga = $0
manga.selected = false
return manga
}
}
インスタンスを再生成するため、①に比べるとパフォーマンスが落ちるのがデメリットです。
ただしほとんどの場合では気になりません。
要素を更新するだけでなく追加や削除まで行うときは、②のほうが関数型チックに書けるためスッキリすることが多いです。
③ポインタを使う
TBD
おわりに
みなさんは配列の要素をまとめて更新する場合、どのような方法を使っているでしょうか?
教えていただけると嬉しいです