Swift 排名词典

Swift ranking dictionary

我能够对字符串和整数字典进行排名。 但我的解决方案看起来并不聪明 "swifty"

问题是当更多的球队与 "team 2" 和 "team 4"

有相同的积分和排名时

ex dic 输入

var dict:[String:Int] = ["team1":79,"team2":5, "team3":18, "team4":5, "team5": 82, "team6":1]

输出

[(team: "team5", rank: 1), (team: "team1", rank: 2), (team: "team3", rank: 3), (team: "team2", rank: 4), (team: "team4", rank: 4), (team: "team6", rank: 5)]

代码:

var ris = [(team:String,rank:Int)]()
var pos = 1

let sorted = dict.sorted(by:{[=12=].value > .value})
print(sorted)
for (i, element) in sorted.enumerated() {

    if i == 0 ||  element.value == sorted[i-1].value {

    }
    else {
        pos += 1
    }
    ris.append((team:element.key,rank:pos))
}
let ranking = ris.sorted(by:{[=12=].rank < .rank})
print(ranking)

打印:

[(team: "team5", rank: 1), (team: "team1", rank: 2), (team: "team3", rank: 3), (team: "team2", rank: 4), (team: "team4", rank: 4), (team: "team6", rank: 5)]

好的,它可以工作,但我确定我错过了一些更好的东西,使用一些排序的闭包,映射 flapmaps 等

有人吗?

您可以通过先在字典中对具有相同分数的团队进行分组来稍微简化一下。然后对字典进行排序(根据分数递减),枚举它(以获得偏移量)并构建排名列表:

let dict:[String:Int] = ["team1":79, "team2":5, "team3":18, "team4":5, "team5": 82, "team6": 1]

let ranking = Dictionary(grouping: dict, by: { [=10=].value })
    .sorted(by: { [=10=].key > .key })
    .enumerated()
    .flatMap { (offset, elem) in
        elem.value.map { (team: [=10=].key, rank: offset + 1 )}
    }

print(ranking)
// [(team: "team5", rank: 1), (team: "team1", rank: 2),
//  (team: "team3", rank: 3), (team: "team2", rank: 4),
//  (team: "team4", rank: 4), (team: "team6", rank: 5)]]

详细解释:

Dictionary(grouping: dict, by: { [=11=].value })

创建一个字典,其键是球队得分,其值是具有该得分的球队的数组。

.sorted(by: { [=12=].key > .key })

按键降序对字典进行排序,结果是一个元组数组:

 [(key: 82, value: [(key: "team5", value: 82)]),
  (key: 79, value: [(key: "team1", value: 79)]),
  (key: 18, value: [(key: "team3", value: 18)]),
  (key: 5, value: [(key: "team2", value: 5), (key: "team4", value: 5)]),
  (key: 1, value: [(key: "team6", value: 1)])]

然后

.enumerated()

从这个数组创建一个惰性序列(偏移量,元素)对:

  (offset: 0, element: (key: 82, value: [(key: "team5", value: 82)])),
  (offset: 1, element: (key: 79, value: [(key: "team1", value: 79)])),
  (offset: 2, element: (key: 18, value: [(key: "team3", value: 18)])),
  (offset: 3, element: (key: 5, value: [(key: "team2", value: 5), (key: "team4", value: 5)])),
  (offset: 4, element: (key: 1, value: [(key: "team6", value: 1)]))

最后 flatMap 在每个(偏移量,元素)对上调用闭包并连接结果。在闭包内,

 elem.value.map { (team: [=16=].key, rank: offset + 1 )}

将一个(偏移量,元素)对映射到一个(团队,等级)元组数组。例如,

  (offset: 3, element: (key: 5, value: [(key: "team2", value: 5), (key: "team4", value: 5)]))

映射到

 [(team: "team2", rank: 4), (team: "team4", rank: 4)]

flatMap() 连接这些数组,给出最终的 ranking 数组。


这是最初发布的解决方案,它为示例数据生成排名 1、2、3、4、4、6(而不是 1、2、3、4、4、5):

let dict:[String:Int] = ["team1":79, "team2":5, "team3":18, "team4":5, "team5": 82, "team6": 1]

var ranking = [(team:String,rank:Int)]()
for (_, list) in Dictionary(grouping: dict, by: { [=19=].value })
    .sorted(by: { [=19=].key > .key }) {
        let pos = ranking.count + 1
        ranking.append(contentsOf: list.map { ([=19=].key, pos )})
}

print(ranking)
// [(team: "team5", rank: 1), (team: "team1", rank: 2),
//  (team: "team3", rank: 3), (team: "team4", rank: 4),
//  (team: "team2", rank: 4), (team: "team6", rank: 6)]

是的,有一个更简单的解决方案:)

let teamScores = [
    "team1":79,
    "team2":5,
    "team3":18,
    "team4":5,
    "team5": 82
]

let teamRanks = teamScores
    .sorted(by: { [=10=].value > .value})
    .enumerated()
    .map { (offset: Int, pair: (team: String, score: Int)) -> (team: String, rank: Int) in
        let rank = offset + 1
        return (team: pair.team, rank: rank)
    }

print(teamRanks)
  • 您对团队分数进行排序,使最高分排在最前面
  • 枚举序列以便您可以访问索引(其中 0 是最好的团队,1 是第二好的团队,...)
  • 在元素上映射重命名元组成员,并将所有偏移量加一(形成基于 1 的索引)

如果你想让并列的分数获得相同的排名,那么类型签名会有点复杂,但想法是相似的:

let teamScores = [
    "team1":79,
    "team2":5,
    "team3":18,
    "team4":5,
    "team5": 82
]

let teamRanks = Dictionary(grouping: teamScores, by: { [=11=].value })
    .sorted(by: { [=11=].key > .key })
    .enumerated()
    .flatMap { (
        offset: Int,
        ranks: (
            commonScore: Int,
            teamScores: [(key: String, value: Int)]
        )
    ) -> [(team: String, rank: Int)] in
        let rank = offset + 1
        return ranks.teamScores.map { (team: [=11=].key, rank: rank) }
    }

print(teamRanks)

我认为.sorted(by:)足以解决这个问题,这是我的解决方案:

// name and score
let dict = [ 
    "Sam": 79, "Joe": 5, "Mary": 79,
    "Tom": 5, "Alex": 82, "Nancy": 1 
] 

// ⭐️ 1. sort it by value first
let sorted = dict.sorted { [=10=].value > .value }

var index = 0
var prev: Int?              // previous score
var currentRank = 0
var rankingList = [(name: String, score: Int, rank: Int)]()

// 2. give every element a rank
for elem in sorted {
    
    // increase index
    index += 1
    
    // if elem.value is not the same as previous score,
    // update previous score and current rank
    if prev == nil || elem.value != prev! {
        prev = elem.value
        currentRank = index
    }
    
    rankingList.append((elem.key, elem.value, currentRank))
}

print(rankingList)
/*
[
    (name: "Alex" , score: 82, rank: 1), 
    (name: "Sam"  , score: 79, rank: 2), 
    (name: "Mary" , score: 79, rank: 2),    // no 3rd place
    (name: "Joe"  , score:  5, rank: 4), 
    (name: "Tom"  , score:  5, rank: 4),    // no 5th place
    (name: "Nancy", score:  1, rank: 6)
]
*/

如果你愿意,你可以为这种情况写一个专门的扩展:

extension Sequence {
    // sequence.statefulMap(_:result:)
    func statefulMap<State, Result>(
        // intial local state
        _ initialState: State, 
        // calculate the result from current element & state,
        // current state may be updated upon each call.
        result: (Element, inout State) -> Result
    ) -> [Result] { 
        var state = initialState
        return map { elem in 
            return result(elem, &state)
        } 
    }
}

// example dictionary
let dict = [
    "Sam": 79, "Joe": 5, "Mary": 79,
    "Tom": 5, "Alex": 82, "Nancy": 1
]

// local state for .statefulMap()
typealias State = (prev: Int?, currentRank: Int, index: Int)
// result type for .statefulMap()
typealias Result = (name: String, score: Int, rank: Int)

let intialState: State = (prev: nil, currentRank: 0, index: 0)

let list = dict
    .sorted { [=11=].value > .value }
    .statefulMap(intialState) { (elem, state: inout State) -> Result in
        // increase index
        state.index += 1
        // if elem.value is not the same as previous score,
        // update previous score and current rank
        if state.prev == nil || elem.value != state.prev! {
            state.prev = elem.value
            state.currentRank = state.index
        }
        return (elem.key, elem.value, state.currentRank)
    }

print(list)

结果是一样的