'Swift dictionary get key for value

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?



Solution 1:[1]

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:[2]

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:[3]

You can use allKeys(for:) if you cast to NSDictionary:

let keys = (dict as NSDictionary).allKeys(for: image) as! [UIImage]

Solution 4:[4]

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)

if let key = someDictionary.first(where: { $0.value == someValue })?.key {
    // use key
}

Swift 5 (extension)

extension Dictionary where Value: Equatable {
    func key(from value: Value) -> Key? {
        return self.first(where: { $0.value == value })?.key
    }
}

Usage

let key = someDictionary.key(from: someValue)) // optional

if let key = someDictionary.key(from: someValue) {
    // non-optional
}

Solution 5:[5]

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:[6]

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:[7]

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:[8]

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:[9]

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:[10]

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:[11]

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

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Leo Dabus
Solution 2 Community
Solution 3
Solution 4
Solution 5
Solution 6 Josh Smith
Solution 7 Teo Sartori
Solution 8 0x416e746f6e
Solution 9 davebcn87
Solution 10
Solution 11 Newbie