Swift dictionary get key for value
SwiftDictionarySwift Problem Overview
I'm using a swift dictionary of type [UIImage:UIImage], and I'm trying to find a specific key for a given value. In Objective-C I could use allKeysForValue, but there appears to be no such method for a Swift dictionary. What should I be using?
Swift Solutions
Solution 1 - Swift
Swift 3: a more performant approach for the special case of bijective dictionaries
If the reverse dictionary lookup use case covers a bijective dictionary with a one to one relationship between keys and values, an alternative approach to the collection-exhaustive filter
operation would be using a quicker short-circuiting approach to find some key, if it exists.
extension Dictionary where Value: Equatable {
func someKey(forValue val: Value) -> Key? {
return first(where: { $1 == val })?.key
}
}
Example usage:
let dict: [Int: String] = [1: "one", 2: "two", 4: "four"]
if let key = dict.someKey(forValue: "two") {
print(key)
} // 2
Solution 2 - Swift
There is, as far as I know, no built-in Swift function to get all dictionary keys for a given value. Here is a possible implementation:
func allKeysForValue<K, V : Equatable>(dict: [K : V], val: V) -> [K] {
return map(filter(dict) { $1 == val }) { $0.0 }
}
The filter
reduces all key-value pairs to those with the given value.
The map
maps the (filtered) key-value pairs to the keys alone.
Example usage:
let dict = ["a" : 1, "b" : 2, "c" : 1, "d" : 2]
let keys = allKeysForValue(dict, 1)
println(keys) // [a, c]
Update for Swift 2: As of Xcode 7 beta 2, this can now be achieved with an extension method for dictionaries of equatable values (thanks to Airspeed Velocity to make me aware of this in a comment):
extension Dictionary where Value : Equatable {
func allKeysForValue(val : Value) -> [Key] {
return self.filter { $1 == val }.map { $0.0 }
}
}
let dict = ["a" : 1, "b" : 2, "c" : 1, "d" : 2]
let keys = dict.allKeysForValue(1)
print(keys) // [a, c]
Update for Swift 3:
extension Dictionary where Value: Equatable {
func allKeys(forValue val: Value) -> [Key] {
return self.filter { $1 == val }.map { $0.0 }
}
}
let dict = ["a" : 1, "b" : 2, "c" : 1, "d" : 2]
let keys = dict.allKeys(forValue: 1)
print(keys) // [a, c]
Solution 3 - Swift
You can use allKeys(for:)
if you cast to NSDictionary
:
let keys = (dict as NSDictionary).allKeys(for: image) as! [UIImage]
Solution 4 - Swift
Understand that most of these answers get the dictionary's entire set of keys before searching through it. This answer uses the first(where:)
method which returns after the key is found.
Swift 5 (inline)
let someValue = UIImage()
if let key = someDictionary.first(where: { $0.value == someValue })?.key {
print(key)
}
Swift 5 (extension)
extension Dictionary where Value: Equatable {
func key(from value: Value) -> Key? {
return self.first(where: { $0.value == value })?.key
}
}
print(someDictionary.key(from: someValue)) // optional
if let key = someDictionary.key(from: someValue) {
print(key) // non-optional
}
Solution 5 - Swift
let dict = ["key1":2,"key2":6,"key3":8,"key4":8]
let searchingValue = 8
let b = dict.filter {$0.value == searchingValue}
let a = b.keys.first
b
provides map with searchingValue
which is ["key4":8,"key3":8]
b.keys.first
provides first element of the all filtered keys which is g
a
is the rquired key for value 8
Solution 6 - Swift
Here's another approach, which I wrote about on my blog. It was tested against Swift 2.2.
extension Dictionary where Value: Equatable {
/// Returns all keys mapped to the specified value.
/// ```
/// let dict = ["A": 1, "B": 2, "C": 3]
/// let keys = dict.keysForValue(2)
/// assert(keys == ["B"])
/// assert(dict["B"] == 2)
/// ```
func keysForValue(value: Value) -> [Key] {
return flatMap { (key: Key, val: Value) -> Key? in
value == val ? key : nil
}
}
}
It's the most efficient implementation posted to this thread that yields all keys mapped to a specified value, because it uses flatMap
, instead of filter
and then map
. I wrote about flatMap
in my Higher-order functions in Swift article, if you're interested.
Also, because my method is generic (by virtue of being in the Dictionary<Key,Value>
generic class) you don't need to cast its result to the key's type, which is necessary when using allKeysForObject(_:)
from NSDictionary
.
Solution 7 - Swift
The Swift 2 version:
func allKeysForValue<K, V : Equatable>(dict: [K : V], val: V) -> [K] {
return dict.filter{ $0.1 == val }.map{ $0.0 }
}
Solution 8 - Swift
If you need to find some key for a value (i.e. not all of them if there is more than one, but just any arbitrary one):
extension Dictionary where Value: Equatable {
func someKeyFor(value: Value) -> Key? {
guard let index = indexOf({ $0.1 == value }) else {
return nil
}
return self[index].0
}
}
Above returns nil
if there is no such key for a value.
Solution 9 - Swift
This will work in case you need any key for a specific value. In my case was the easiest approach. Hope that it helps:
Swift 4.1+
extension Dictionary where Key == String, Value: Equatable {
func key(for value: Value) -> Key? {
return compactMap { value == $1 ? $0 : nil }.first
}
}
Solution 10 - Swift
If you don't know that the values are unique, this is an option:
public extension Dictionary where Value: Equatable {
/// The only key that maps to `value`.
/// - Throws: `OnlyMatchError`
func onlyKey(for value: Value) throws -> Key {
try onlyMatch { $0.value == value } .key
}
}
public extension Sequence {
/// The only match for a predicate.
/// - Throws: `OnlyMatchError`
func onlyMatch(for getIsMatch: (Element) throws -> Bool) throws -> Element {
guard let onlyMatch: Element = (
try reduce(into: nil) { onlyMatch, element in
switch ( onlyMatch, try getIsMatch(element) ) {
case (_, false):
break
case (nil, true):
onlyMatch = element
case (.some, true):
throw onlyMatchError.moreThanOneMatch
}
}
) else { throw onlyMatchError.noMatches }
return onlyMatch
}
}
/// An error thrown from a call to `onlyMatch`.
public enum OnlyMatchError: Error {
case noMatches
case moreThanOneMatch
}
XCTAssertEqual(
try ["skunky": "monkey", "🦨": "🐒"].onlyKey(for: "🐒"),
"🦨"
)
Solution 11 - Swift
You can just filter through the dictionary and find the value that matches the value you're looking for and then get all the keys that are paired with that value in the dictionary.
FYI: someValue
in this answer represents the hypothetical value you're looking for
dictionary.filter{$0.value == someValue}.keys