【问题标题】:Turn array of object into cummulative dictionary Swift将对象数组转换为累积字典 Swift
【发布时间】:2020-06-06 23:27:43
【问题描述】:

我有一个名为person的结构

struct person{
   var name : String
   var score: Int
}

然后我创建一个名为 let people : [person] = [person("a", 1), person("a", 3), person("b", 5)]

如您所见,这里有两个同名“a”的对象。

现在我想把这个变成一个累积字典,显示每个人的总分。在这种情况下 dict = {"a": 4 (3+1), "b": 5}

我知道我违反了 OO 设计规则。 谢谢

【问题讨论】:

  • 顺便说一下,在 Swift 中类型名称应该是 UpperCamelCase
  • let people = [("a",1), ("a",3), ("b", 5)].map(Person.init)

标签: arrays swift dictionary


【解决方案1】:

这里有一个基本的建模问题。您的结构 person 实际上并没有为人建模。它模拟RoundResult 之类的东西。

我将通过创建一个真正模拟人的Player 来重构它(只有name: String 之类的字段),并创建一个包含winner: Playerscore: ScoreRoundResult

struct Player: Hashable { // Perhaps should be a class, if names aren't unique.
    let name: String
}

struct RoundResult {
    let winner: Player
    let score: Int
}

let playerA = Player(name: "a")
let playerB = Player(name: "b")

let roundResults = [
    RoundResult(winner: playerA, score: 1),
    RoundResult(winner: playerA, score: 3),
    RoundResult(winner: playerB, score: 5),
]

let scoresByPlayer = Dictionary(grouping: roundResults, by: \.winner)
    .mapValues { roundResults -> Int in
        let scores = roundResults.lazy.map(\.score)
        return scores.reduce(0, +)
    }

print(scoresByPlayer)

从这里,您可以在玩家上添加一个 score 变量,实际上模拟玩家得分,而不仅仅是单轮/游戏/比赛/任何事情中的一小部分 p>

【讨论】:

  • 如果此答案解决了您的问题,请使用左侧的复选标记将其标记为已接受
【解决方案2】:

您可以通过两步来实现:1) 分组到字典中,2) 对分组的分数求和:

// grouping = { "a": [person("a", 1), person("a", 2)], "b": [person("b": 3)]
let grouping = Dictionary.init(grouping: people, by: { person in person.name })

let dict = grouping.mapValues { group in 
   group.reduce(0, { sum, person in sum + person.score })
}

或者更短但更神秘的形式:

let d = Dictionary.init(grouping: people, by: { $0.name })
                  .mapValues{ $0.reduce(0, $0 + $1.score ) }

【讨论】:

  • let dict: [String: Int] = .init(people.map{($0.name, $0.score)}, uniquingKeysWith: +)
  • @LeoDabus 不错!更好
【解决方案3】:

我假设您是 Swift 新手。让我带你看一下这个的几个迭代。

最基本的是直截了当的方法。

var dict = [String: Int]()

for person in people {
    if let score = dict[person.name] {
        // If the person already has a score in dict, then add this new score to it.
        dict[person.name] = score + person.score
    } else {
        // If this is the first score for this person, then add the person to dict.
        dict[person.name] = person.score
    }
}

接下来,我们将使用subscript(_:default:) 运算符将if 条件的两部分结合起来。

var dict = [String: Int]()

for person in people {
    // Add the new score to the person's existing score. If there is no existing
    // score, then default to 0 and add the new score.
    dict[person.name, default: 0] += person.score
}

最后,使用 reduce(into:_:) 摆脱 for 循环(参见 no raw loops segment of C++ Seasoning

// Reduce people into a dictionary of names and cumulative scores.
let dict = people.reduce(into: [String: Int]()) { result, person in
    result[person.name, default: 0] += person.score
}

【讨论】:

  • 非常感谢杰弗里。我一直在寻找如何使用 reduce 就像你向我展示的那样。这对我帮助很大。
猜你喜欢
  • 1970-01-01
  • 2018-11-22
  • 2015-11-05
  • 1970-01-01
  • 2021-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多