【问题标题】:Swift dictionary get key for valueSwift字典获取值的关键
【发布时间】:2014-11-30 21:51:26
【问题描述】:

我正在使用 [UIImage:UIImage] 类型的 swift 字典,并且我正在尝试为给定值查找特定键。在 Objective-C 中,我可以使用 allKeysForValue,但 Swift 字典似乎没有这样的方法。我应该使用什么?

【问题讨论】:

    标签: swift dictionary


    【解决方案1】:

    Swift 3:一种针对双射字典特殊情况的性能更高的方法

    如果反向字典查找用例涵盖键和值之间具有一对一关系的双射字典,则收集穷举filter 操作的另一种方法是使用更快的短路方法来查找 一些键,如果存在的话。

    extension Dictionary where Value: Equatable {
        func someKey(forValue val: Value) -> Key? {
            return first(where: { $1 == val })?.key
        }
    }
    

    示例用法:

    let dict: [Int: String] = [1: "one", 2: "two", 4: "four"]
    
    if let key = dict.someKey(forValue: "two") { 
        print(key)
    } // 2
    

    【讨论】:

    • 太棒了。谢谢。
    【解决方案2】:

    据我所知,没有内置的 Swift 函数来获取所有字典键 对于给定的值。这是一个可能的实现:

    func allKeysForValue<K, V : Equatable>(dict: [K : V], val: V) -> [K] {
        return map(filter(dict) { $1 == val }) { $0.0 }
    }
    

    filter 将所有键值对减少为具有给定值的键值对。 map 将(过滤的)键值对映射到单独的键。

    示例用法:

    let dict = ["a" : 1, "b" : 2, "c" : 1, "d" : 2]
    let keys = allKeysForValue(dict, 1)
    println(keys) // [a, c]
    

    Swift 2 更新:从 Xcode 7 beta 2 开始,现在可以实现 具有等值字典的扩展方法 (感谢Airspeed Velocity 让我在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]
    

    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]
    

    【讨论】:

    • 有没有办法将值作为字符串而不是数组返回?在我的设置中,每个键始终只有一个唯一值(它是从日文到罗马字的字典)。
    • 只是想我会提到我在我的项目中使用这种方法有一段时间了,今天在分析时注意到它可能非常慢...寻找替代品。
    • @HashemAboonajmi:感谢您的编辑。但是,我决定恢复到以前的版本,因为我认为即使在 Swift 3 中allKeys(forValue:) 也是正确的。具有类似命名的现有方法是index(forKey:)update(value: forKey)value(forKey:)
    【解决方案3】:

    如果您转换为NSDictionary,则可以使用allKeys(for:)

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

    【讨论】:

    • 谢谢,效果很好,而且是一个很好的干净解决方案。
    • 只是一个小小的补充,我用这段代码作为一个模式从字典中提取一个 Int 键:let keys = (ItemDict as NSDictionary).allKeysForObject(orderItems[indexPath.row]) 然后你可以访问:let someIntValue = keys[0].integerValue; 像梦一样工作,谢谢!跨度>
    • 斯威夫特 3:let keys = (dict as NSDictionary).allKeys(for: image) as! [UIImage]
    【解决方案4】:

    了解这些答案中的大多数会在搜索字典之前获取字典的整个键集。此答案使用找到密钥后返回的first(where:) 方法。

    Swift 5(内联)

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

    Swift 5(扩展)

    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
    }
    

    【讨论】:

    • 太棒了!这应该是当前的正确答案。
    • @Adrian 请注意,此答案使用与 accepted 答案完全相同的方法,该答案在此答案之前 3 年发布。然而,这个答案没有提到应该小心使用它,明确知道它会产生 some 键,特别是在几个键存储相同值的字典中。
    【解决方案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 提供带有 searchingValue 的地图,即["key4":8,"key3":8]

    b.keys.first 提供所有过滤键的第一个元素,即 g

    a 是值 8

    的所需键

    【讨论】:

    • 添加一些关于你的答案的解释
    【解决方案6】:

    这是另一种方法,我写了关于on my blog。它针对 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
        }
      } 
    }
    

    这是发布到此线程的最有效的实现,它产生映射到指定值的所有键,因为它使用flatMap,而不是filter,然后是map。如果您有兴趣,我在我的Higher-order functions in Swift 文章中写过flatMap

    另外,因为我的方法是通用的(由于在Dictionary&lt;Key,Value&gt; 通用类中),您不需要将其结果转换为键的类型,这在使用NSDictionary 中的allKeysForObject(_:) 时是必需的。

    【讨论】:

    • 谢谢你,我发现这是完成这项任务的一种非常干净的方式。
    【解决方案7】:

    Swift 2 版本:

    func allKeysForValue<K, V : Equatable>(dict: [K : V], val: V) -> [K] {
        return dict.filter{ $0.1 == val }.map{ $0.0 }
    }
    

    【讨论】:

    • 为什么是$0.1$0.0 而不是$0$1
    • 因为您正在映射字典,所以 $0 是字典元素。 $0.0 是键,$0.1 是值。
    • 对不起,我不明白你的意思。您说$0 是字典元素,但keyvalue 是字典元素,对吧?因为dictionary = [key:valule].
    • 你的意思是$0 是字典中的第一个元素吗?喜欢[firstKey:firstValue]
    • 在 Swift 中字典被实现为通用集合。集合的内容是通过迭代其元素来访问的。在字典的情况下,这些元素被定义为键值对的元组。您可以使用索引访问元组的各个部分,因此 $0.0 对应于第一个(键),$0.1 对应于第二个(与键相关的值)。
    【解决方案8】:

    如果您需要为一个值找到 some 键(即,如果有多个,则不是 all,而是任意一个):

    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 返回nil

    【讨论】:

      【解决方案9】:

      如果您需要特定值的任何键,这将起作用。在我的情况下是最简单的方法。希望对你有帮助:

      斯威夫特 4.1+

      extension Dictionary where Key == String, Value: Equatable {
          func key(for value: Value) -> Key? {
              return compactMap { value == $1 ? $0 : nil }.first
          }
      }
      

      【讨论】:

        【解决方案10】:

        如果您不知道这些值是唯一的,这是一个选项:

        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: "?"),
          "?"
        )
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-11-02
          • 2018-03-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-12-17
          • 1970-01-01
          相关资源
          最近更新 更多