[Swift] Dictionary をデフォルト値付きで扱う方法

Swift

Dictionary をデフォルト値付きで使う方法を説明します

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Monterey 12.1 Beta
  • Xcode 13.1
  • iOS 15

Dictionary

Swift の Standard Library で用意されている型です。

Apple のドキュメントは、こちら

何らかの値を キー とひもづけて保持しておくことができるコレクションの1つです。

キーは、Hashable であることが必要ですが、定数時間で要素を取得することができます。

存在しないキーの扱い

存在しないキーを使用して値を取得しようとすると nil が返ります。


let dic = [1: "One", 3:"Three"]
print(dic[1]) // One
print(dic[2]) // nil

存在しないキーにデフォルト値を持たせる

キーにひもづけられた値が存在しない時には、nil が返されますが、nil の代わりに 指定したデフォルト値を返すことができます。

Apple のドキュメントは、こちら


let dic = [1: "One", 3:"Three"]
print(dic[1], default: "I don't know") // One
print(dic[2], default: "I don't know") // I don't know

受け取った側で nil チェックをすることで、同様のことはできますが、シンプルに記述でき わかりやすいコードを書くことができます。

以下は、すこし考察してみたことです。

デフォルト値を持たせたいケース

アプリケーションを作成していて、要素に対して付加的な情報を記憶しておきたい時があります。

モデル要素に付加情報を短期的に記録したいケース(例えば色指定)

特に具体的なユースケースがあるわけではありませんが、例えば、モデルデータにアプリとして付加情報を短期的(例えばそのアプリの実行中のみ)に記憶しておきたいとします。

元のモデルデータに新しい属性を追加することも1つの解決策ですが、個別アプリの必要とする情報を記憶させるためだけに、モデルを拡張していくのはあまりよろしくありません。

以下は、Person の配列を保持しているモデルで、それぞれの Person に対して 指定した色を保持したいとします。

指定色を Array に記録する

Model で保持している PersonList と同じ並びの ColorList を ViewModel に List として定義して保持するようにしました。


import Foundation
import SwiftUI

struct Person: Identifiable {
    let id = UUID()
    var givenName: String
    var familyName: String
}

struct Model {
    var personList:[Person] = []
}

class ViewModel {
    var model: Model
    var colorList:[Color]
    
    init(_ model: Model) {
        self.model = model
        self.colorList = Array(repeating: Color.white, count: model.personList.count)
    }
    
    func setPersonColor(id: Person.ID, color: Color) {
        if let index = model.personList.firstIndex(where: {$0.id == id}) {
            self.colorList[index] = color
        }
    }

    func personColor(id: Person.ID) -> Color {
        if let index = model.personList.firstIndex(where: {$0.id == id}) {
            return self.colorList[index]
        }
        return .white // default color even for invalid person.id
    }
}
考察

初期化としてデフォルト色(白)をセットする処理を init で行っていますので、数が増えた時が気になります。

色設定時も、設定色を返す時も、Array を探索する必要がありますので、要素数に応じて処理時間がかかっています。

指定色を Dictionary に記録する

上記の方法でも良いのですが、Dictionary をうまく使って実装することもできます。

ViewModel には、colorDic[Person.ID:Color] という Dictionary 型のインスタンスプロパティを追加します。


class ViewModel {
    var model: Model
    var colorDic:[Person.ID: Color] = [:]
...

init では、初期化する必要はありません。

setPersonColor は、以下のようになります。


    func setPersonColor(id: Person.ID, color: Color) {
        self.colorDic[id] = color
    }

ID をキーにしている Dictionary を使用しているので、検索する必要はありません。

personColor は、以下のようになります。


    func personColor(id: Person.ID) -> Color {
        self.colorDic[id, default: .white]
    }

設定色を返す時にも、Array 等を検索する必要はなく、Dictionary の該当キーの値を返すだけです。

ポイント

デフォルトの値があるケースでは、Dictionary[key, default:] をうまく使うことができるケースです。

空の辞書から始めて 必要に応じて要素を追加保存していくことで、コンパクトなコードにすることができます。

まとめ:Dictionary をデフォルト値付きで扱う方法

Dictionary をデフォルト値付きで扱う方法
  • Dictionary の [key, default:] をうまく使うと デフォルト値付きの Dictionary として扱うことができる

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です