【问题标题】:Swift - Convert Array to DictionarySwift - 将数组转换为字典
【发布时间】:2018-11-22 03:42:38
【问题描述】:

我只想将玩家姓名数组转换为字典记分牌,给每个人的初始分数为 0。

意思...

var playerNames = ["Harry", "Ron", "Hermione"]

变成...

var scoreBoard: [String:Int] = [ "Ron":0, "Harry":0, "Hermione":0 ]

这是我第一次提出问题,但我完全被什么感觉如此简单,而且我发现的所有操作方法/问题都以某种方式关闭。我尝试过以各种方式使用 reduce,但总是以短的方式结束。提前致谢!

【问题讨论】:

    标签: arrays swift dictionary


    【解决方案1】:

    这是另一种方法:

    // Implementation 
    
    extension Dictionary {
        static func from(_ array: [Value], key: KeyPath<Value, Key>) -> Dictionary<Key, Value> {
            var dict: Dictionary<Key, Value> = [:]
            array.forEach { dict[$0[keyPath: key]] = $0}
            return dict
        }
    }
    
    
    /// Usage
    
    let array: [String] = ["Banana", "Apple"]
    
    Dictionary.from(array, key: \.self)
    
    // or if you have a more complex object
    
    struct Foo {
        let id: Int
    }
    
    
    let array2: [Foo] = [Foo(id: 1), Foo(id: 2)]
    
    Dictionary.from(array2, key: \.id)
    
    

    【讨论】:

      【解决方案2】:

      基于jmad8的回答

      详情

      • 斯威夫特 5.3
      • Xcode 12.0.1 (12A7300)

      解决方案

      extension Sequence {
          func toDictionary<Key: Hashable, Value>(where closure: (Element) -> (Key, Value)) -> [Key: Value] {
              reduce(into: [Key: Value]()) { (result, element) in
                  let components = closure(element)
                  result[components.0] = components.1
              }
          }
          
          func toCompactDictionary<Key: Hashable, Value>(where closure: (Element) -> ((Key, Value)?)) -> [Key: Value] {
              reduce(into: [Key: Value]()) { (result, element) in
                  guard let components = closure(element) else { return }
                  result[components.0] = components.1
              }
          }
      }
      

      用法

      // Sample 1
      
      print(languages.toDictionary { (string) -> (Character, String) in
          return (string.first!, string)
      })
      
      print(languages.toCompactDictionary { (string) -> (Character, String)? in
          guard let character = string.first, character != Character("J") else { return nil }
          return (character, string)
      })
      
      
      // Sample 2
      
      print(languages.enumerated().toDictionary { (data) -> (Int, String) in
          return (data.offset, data.element)
      })
      
      // Shorter version of sample 2
      
      print(languages.enumerated().toDictionary { ($0.offset, $0.element) })
      
      // Sample 3
      
      struct Order {
          let id: Int
          let desctiption: String
      }
      
      let orders = [
                      Order(id: 0, desctiption: "Apple"),
                      Order(id: 1, desctiption: "Banana"),
                      Order(id: 2, desctiption: "watermelon")
                  ]
      
      print(orders.toDictionary { ($0.id, $0) })
      

      【讨论】:

        【解决方案3】:

        如果已知玩家名称完全不同,那么您可以这样做

        let playerNames = ["Harry", "Ron", "Hermione", "Ron"]
        
        var scoreBoard = Dictionary(uniqueKeysWithValues: zip(playerNames,
                                                              repeatElement(0, count: playerNames.count)))
        
        print(scoreBoard) // ["Harry": 0, "Ron": 0, "Hermione": 0]
        

        这里zip 用于创建玩家/分数对的序列,从中创建字典。

        备注: 最初我使用AnySequence { 0 } 来生成零。使用repeatElement() 代替suggested by Alexander 具有将正确所需容量传递给字典初始化程序的优点。

        【讨论】:

          【解决方案4】:

          reduce 绝对是更难正确使用的内置函数之一,但这正是您想要的。

          let names = ["Harry", "Ron", "Hermione"]
          let scoreboard: [String: Int] = names.reduce(into: [:], { result, next in
              result[next] = 0
          })
          

          它有两个参数:初始值(在我们的例子中,一个空字典[:]),以及一个用数组中的每个元素更新结果的闭包。这个闭包有 2 个参数,resultnext。我们想根据next 元素更新result。我们的闭包通过将 result[next] 设置为 0 来实现这一点。

          【讨论】:

            【解决方案5】:

            使用 reduce(into:_:)

            var playerNames = ["Harry", "Ron", "Hermione"]
            
            let playerScore = playerNames.reduce(into: [:]) { counts, playerNames in
                counts[playerNames, default: 0] += 0 
            }
            
            print(playerScore)
            

            要记录球员姓名(例如重复姓名):

            counts[myArray, default: 0] += 1
            

            例如,如果罗恩在比赛开始前有两个条目(分数 > 0),那么你就会知道。

            不使用reduce(into:_:) 方法并作为扩展:

            var playerNames = ["Harry", "Ron", "Hermione"]
            
            extension Sequence where Self.Iterator.Element: Hashable {
            
            func freq() -> [Self.Iterator.Element: Int] {
                return reduce([:]) {
                        ( iter: [Self.Iterator.Element: Int], element) in
                        var dict = iter
                        dict[element] = 0
                        return dict
                    }
                }
            }
            
            print(playerNames.freq())
            // ["Harry": 0, "Hermione": 0, "Ron": 0]
            

            记数(例如重复的名字):

            dict[element, default: -1 ] += 1
            

            【讨论】:

            • ... 0 - 1) + 1?但为什么呢?
            • @Alexander:否则所有元素都从 1 开始计数。我想-1 可以代替...
            • 我不认为 OP 希望对元素进行基于 0 的计数。我认为所有条目都应该只有 0 的值。
            • dict[element] = (dict[element] ?? 0 - 1) + 1 可以简化为dict[element, default: -1] += 1。 – dict[element] = (dict[element] ?? 0) 可以简化为 dict[element] = 0 。 – 但请注意,您的方法会创建几个中间字典,reduce(into: ...) 可以避免这种情况
            【解决方案6】:

            这是我喜欢使用的一种快速的衬里:

            let scoreboard = playerNames.reduce(into: [String: Int]()) { $0[$1] = 0 }
            

            【讨论】:

            • 伙计,我希望我能最喜欢的答案,因为这很漂亮
            • 使用类型推断来缩短这个时间……let scoreboard = playerNames.reduce(into: [:]) { $0[$1] = 0 }
            • 这部分是什么意思/做什么? $0[$1] = 0
            【解决方案7】:

            您可以使用reduce(into:),正如您所怀疑的那样。您只需要将初始值[String:Int]() 声明为所需类型的空Dictionary,然后只需将playerNames 中所有键的值设置为0。

            var playerNames = ["Harry", "Ron", "Hermione"]
            var scoreBoard = playerNames.reduce(into: [String:Int](), { currentScores,player in
                currentScores[player] = 0
            })
            

            【讨论】:

              【解决方案8】:

              您可以这样做:

              var playerNames = ["Harry", "Ron", "Hermione"]
              
              var dictionary = [String: Int]()
              for player in playerNames {
                  dictionary[player] = 0
              }
              

              【讨论】:

                猜你喜欢
                • 2021-10-18
                • 1970-01-01
                • 2017-02-24
                • 1970-01-01
                • 1970-01-01
                • 2016-05-15
                • 2018-01-08
                • 1970-01-01
                • 2018-11-26
                相关资源
                最近更新 更多