前置き: Swift5における変更点
コメントで頂きましたが、DictionaryLiteral
という名称はSwift5でDeprecatedとなり、KeyValuePairs
にRenameされました。(@imaizumeさんありがとうございます!)
PRを見た感じだと完全にRenameだけで機能は変わっていないようです。
Dictionary
でもLiteral
でもないバッドネーミングだから変えたとのこと。
この記事はSwift3時点で書かれたものなので、以降DictionaryLiteral
と古い呼称で記述しておりますがご容赦ください。
DictionaryLiteralとは
SwiftのDictionaryはハッシュテーブルなので、素早いルックアップが可能ですが、イテレートした際に順序が保証されません。
let dict = ["0": 0, "1": 1, "2": 2, "3": 3]
for (key, value) in dict {
print("\(key), \(value)")
}
出力はこうなります。
2, 2
1, 1
0, 0
3, 3
順番がバラバラになりました。
では、どうしても辞書の順序を保存しておきたいときはどうしたら良いでしょうか。実は、順序付きの辞書は用意されています。
DictionaryLiteralというのがそれです。ExpressibleByDictionaryLiteralに準拠しているため、型を明示すれば辞書のリテラルをそのまま代入できます。
let dict: DictionaryLiteral = ["0": 0, "1": 1, "2": 2, "3": 3]
for (key, value) in dict {
print("\(key), \(value)")
}
0, 0
1, 1
2, 2
3, 3
順番が保たれています。また、DictionaryLiteralはRandomAccessCollectionなので、配列のように添え字アクセスすることも可能です。キーとバリューのタプルが返ってきます。
print(dict[1])
("1", 1)
ただし、DictionaryLiteralはDictionaryと同じように使えるわけではありません。残念ながら、少なくとも機能的な面では、DictionaryLiteralは[(String, Int)]
のようにタプルの配列を書くのとほとんど変わりません。
例えば、キーを指定して値を取得することはできません。指定したキーに対応する値を取得するにはこのようにします。
let dict: DictionaryLiteral = ["0": 0, "1": 1, "2": 2, "3": 3]
if let index = dict.index(where: { $0.0 == "1" }) {
let value = dict[index].1
print("value: \(value)") // value: 1
}
また、同じキーが複数ある状態も許容してしまいます。
var dict: DictionaryLiteral = ["0": 0, "0": 1, "0": 2, "0": 3]
if let index = dict.index(where: { $0.0 == "1" }) {
let value = dict[index].1
print("value: \(value)") // value: 1
}
ではどういう時にDictionaryLiteralを使うと良いのでしょうか。ドキュメントにはこうあります。
Use a DictionaryLiteral instance when you need an ordered collection of key-value pairs and don’t require the fast key lookup that the Dictionary type provides.
- 順序つき辞書が必要なとき
- 高速なルックアップが不要なとき
メリットとしては名前の通り、Dictionaryのリテラルで書けて可読性が上がるということなどでしょうか。順序付きの辞書が欲しくなったときに、検討してみてはいかがでしょうか。