LoginSignup
19
11

More than 3 years have passed since last update.

配列の要素をまとめて更新する方法(Swift)

Last updated at Posted at 2021-03-19

はじめに

以下の構造体があります。

Manga.swift
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 エラーが発生してビルドできません。
スクリーンショット 2021-03-19 19.55.13.png

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

おわりに

みなさんは配列の要素をまとめて更新する場合、どのような方法を使っているでしょうか?
教えていただけると嬉しいです :relaxed:

参考リンク

19
11
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
11