# 辞書 | Dictionary型

辞書とは、キーと値のペアで構成され、順序を持たないコレクションです。各値は一意のキーに関連付けられ、キーは辞書内でその値の識別子として機能します。配列とは異なり、辞書内の項目には指定された順序はありません。Swiftでは、Dictionary<Key, Value>型 で辞書を表現します。

# 辞書の宣言方法

Swiftの辞書は次のフォーマットで書くことができます。

let dictionay: Dictionary<Keyの型, Valueの型> = ["キー名": 値]

次の例では、キーをString型、値にInt型を持つ辞書を定義しています。

let a: Dictionary<String, Int> = ["key": 1, "key2": 2]

# シンタックシュガー

Dictionary<String, Int> の型アノテーションは、シンタックシュガーを使って、[キーの型: 値の型] で書くことができます。

let a: [String: Int] = ["key": 1, "key2": 2]

一般的に、シンタックスシュガーが使われることが多いです。

# 型推論

Swiftは、辞書に含むキーと値から型を推論できるので、宣言時には型を省略して書くことができます。前節の例を型アノテーションを省略して書くと、次のようになります。

let a = ["key": 1, "key2": 2]

しかし、空の辞書の場合は、値の型推論ができないため、明示的に型アノテーションを指定する必要があります。

let a: [String: Int] = [:]

# 空の辞書

空の辞書をイニシャライザを使って、次のように書くこともできます。

let a = [String: Int]()

# 辞書の特徴

Swiftの辞書の特徴は以下になります。

# キーは一意にならなければならない

キーはコレクションの中で、他のキーと被らないようにしなければなりません。

同じキーを指定した場合は、実行時エラーになります。

let a: [String: Int] = ["key": 1, "key": 2]

/* 実行結果 */
// Fatal error: Dictionary literal contains duplicate keys
// Current stack trace:

# 順序関係は保証されない

辞書は、繰り返される要素の順番を保証しません。

for-in文で、繰り返し処理を実行すると、実行環境によってその順番が変わります。

let dictionary = ["1":10, "2":20, "3":30, "4":40, "5":50]

for (key, value) in dictionary {
  print("\(key): \(value)")
}

// 実行環境によって順番は変わる
// 1回目
// 4: 40
// 1: 10
// 2: 20
// 5: 50
// 3: 30

// 2回目
// 2: 20
// 1: 10
// 3: 30
// 4: 40
// 5: 50

順番を保証したい場合は、KeyValuePairs 型 を使用してください。

(Swift5.0 以前は DictionaryLiteral という名称でしたがSwift5.0以降では、KeyValuePairs という名前になりました。)

// KeyValuePairs型を指定する
let dictionary: KeyValuePairs = ["1":10, "2":20, "3":30, "4":40, "5":50]

for (key, value) in dictionary {
  print("\(key): \(value)")
}

// 1回目
// 1: 10
// 2: 20
// 3: 30
// 4: 40
// 5: 50

// 2回目
// 1: 10
// 2: 20
// 3: 30
// 4: 40
// 5: 50

# 値へのアクセス

辞書の値へアクセスするには、辞書[キー名] のフォーマットでキー名を指定すると、取得することができます。

let a = ["key": 1, "key2": 2]

let b = a["key"] // Optional(1)

存在しないキー名を指定すると、nilが返ってきます。

let a = ["key": 1, "key2": 2]

let b = a["key3"] // nil

nilになりうる値は Optional型 になります。そのため、辞書へアクセスして取り出した値は、Optional型で返ってきます。

# 値の更新

辞書の値を更新するには、更新したい値のキー名を指定して値を代入すると、更新することができます。

var a = ["key": 1, "key2": 2]

a["key"] = 100

a // ["key": 100, "key2": 2]

定数の let で宣言すると、代入が不可になるのでコンパイラエラーになります。

let a = ["key": 1, "key2": 2]

a["key"] = 100

/* 実行結果 */
// error: cannot assign through subscript: 'a' is a 'let' constant
// a["key"] = 100
// note: change 'let' to 'var' to make it mutable
// let a = ["key": 1, "key2": 2]

# 値の追加

辞書の要素を追加するには、追加したい値のキー名を指定して値を代入すると、追加することができます。

var a = ["key": 1, "key2": 2]

a["key3"] = 3

a  // ["key": 1, "key3": 3, "key2": 2]

# 値の削除

辞書の要素を削除するには、追加したい値のキー名を指定して nil を代入すると、削除することができます。

var a = ["key": 1, "key2": 2]

a["key"] = nil

a // ["key2": 2]

# removeValue

removeValue も同様に辞書の要素を削除することができます。削除対象の要素が存在すれば、削除した値を返します。もし存在しなければnilを返します。

var a = ["key": 1, "key2": 2]

let removed = a.removeValue(forKey: "key")

removed       // Optional(1)
a             // ["key2": 2]

# その他の使い方

# グループ化する

与えられたキーを元に配列などのSequenceからグループ化された辞書を生成することができます。初期化する際に、groupingby 引数を指定してグループ化させます。

次の例では、生徒をクラスごとに分類してグループ化しています。grouping には対象の配列を渡し、by にはグループ化させるキーを指定します。

struct Student {
  var name: String
  var className: String
}

let students = [
  Student(name: "Taro", className: "A"),
  Student(name: "Jiro", className: "B"),
  Student(name: "Saburo", className: "C"),
  Student(name: "Shiro", className: "A"),
  Student(name: "Goro", className: "C"),
]

let grouped = Dictionary(grouping: students, by: { $0.className })

grouped["A"] // [{name "Taro", className "A"}, {name "Shiro", className "A"}]