Dictionaryにvalueを投げてkey(keys)を取得する

Monday, February 1, 2016

通常はDictionaryにkeyを投げて対応するvalueを取得しますが、
valueを投げて対応するkey(keys)を取得してみます。


実はObjective-Cの時から、NSDictionaryクラスに、 allKeysForObject という、valueを投げて対応するkeyの配列を返してくれるメソッドがあるので、それに倣って書いてみます。


実装

extension Dictionary where Value: Equatable {
    func allKeysForValue(value: Value) -> [Key] {
        return self.filter({ $0.1 == value }).map({ $0.0 })
        // return self.flatMap({ $0.1 == value ? $0.0 : nil }) // こっちでもok
    }
    func keyForValue(value: Value) -> Key? {
        return allKeysForValue(value).first
    }
}

Filter→Mapが素直な感じですが、計算量が結構大きくなる可能性があるので、
flatMapで一発で済ませるのもアリです。

こんな感じで取得できます。

let list = [
    "Apple" : 10001,
    "Banana" : 10002,
    "Candy" : 10003,
]

//------

print(list.allKeysForValue(10001)) // ["Apple"]
print(list.keyForValue(10002)) // Optional("Banana")
print(list.keyForValue(10010)) // nil

注意としては、keyは重複しないので、keyを投げて取得できるvalueは一意に定まりますが、valueはそうとは限らないので、対応するkeyが複数取得できる場合があります。例えば、

let list = [
    "Apple" : 10001,
    "Banana" : 10002,
    "Candy" : 10003,
    "Desert" : 10001,
]

に対して10001を投げてみると、["Apple","Desert"]で返却されます。
その場合のkeyForValueは最初にヒットした方を返すようにしているので、そっちを使うと"Desert"は見逃すことになります。
一意に定められるのか、複数ありえるのかで使用する関数を変えれば良いかと思います。


余談

こういうのが必要!ってなったら、無理にDictionaryのKeyとValueでやろうとしないで、 素直にstructで構造体を作ってあげた方が色々と処理しやすいかと思います。

techSwiftiOStips

Hugoで下書き状態のmarkdownをチェックするshellスクリプト

Objective-C+Generics+typedef