0

I have structure called person

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

Then I create an array called let people : [person] = [person("a", 1), person("a", 3), person("b", 5)]

As you can see, there are two objects with the same name "a" here.

Now I would like to turn this one into a cumulative dictionary, that shows the total score of each person. In this case dict = {"a": 4 (3+1), "b": 5}

I know I'm violating the OO design rules. Thanks

2
  • 2
    by the way, type names should be UpperCamelCase in Swift. Commented Jun 6, 2020 at 23:55
  • let people = [("a",1), ("a",3), ("b", 5)].map(Person.init) Commented Jun 7, 2020 at 0:22

3 Answers 3

1

There's a fundamental modelling issue here. Your struct person doesn't actually model a person. It models something like a RoundResult.

I would refactor this by making a Player that truly models a person, (with only fields like name: String), and make a RoundResult that contains a winner: Player and a score: Score.

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)

From here, you can add a score variable on player, which actually models the players score, not just a single sliver of it from a single round/game/match/whatever

Sign up to request clarification or add additional context in comments.

1 Comment

If this answer has solved your question, please mark it as accepted using the checkmark on the left
0

You can achieve this in two steps: 1) group into a dictionary, and 2) sum scores of the grouping:

// 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 })
}

Or in its shorter, but more cryptic form:

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

2 Comments

let dict: [String: Int] = .init(people.map{($0.name, $0.score)}, uniquingKeysWith: +)
@LeoDabus Nice! Better
0

I'm assuming you are new to Swift. Let me walk you though a few iterations of this.

The most basic is the straight forward approach.

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
    }
}

Next, we will used subscript(_:default:) operator to combine the two parts of the if conditional.

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
}

Finally, use reduce(into:_:) to get rid of the for loop (see 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
}

1 Comment

Thank you so much Jeffery. I was looking for how to use reduce like you showed me. It helped me a lot.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.