Swift dictionary get key for value

SwiftDictionary

Swift 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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmginnView Question on Stackoverflow
Solution 1 - SwiftdfribView Answer on Stackoverflow
Solution 2 - SwiftMartin RView Answer on Stackoverflow
Solution 3 - SwiftvacawamaView Answer on Stackoverflow
Solution 4 - SwiftliquidView Answer on Stackoverflow
Solution 5 - SwiftLearnerView Answer on Stackoverflow
Solution 6 - SwiftJosh SmithView Answer on Stackoverflow
Solution 7 - SwiftTeo SartoriView Answer on Stackoverflow
Solution 8 - Swift0x416e746f6eView Answer on Stackoverflow
Solution 9 - Swiftdavebcn87View Answer on Stackoverflow
Solution 10 - SwiftJessyView Answer on Stackoverflow
Solution 11 - SwiftNewbieView Answer on Stackoverflow