【问题标题】:Swift ranking dictionarySwift 排名词典
【发布时间】:2019-10-24 02:34:21
【问题描述】:

我能够对字符串和 int 的字典进行排名。 但我的解决方案看起来并不聪明且“迅速”

问题是当更多的团队与“团队 2”和“团队 4”具有相同的积分和相同的排名时

ex dic 输入

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

输出

[(团队:“团队5”,排名:1),(团队:“团队1”,排名:2),(团队:“团队3”, 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:{$0.value > $1.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:{$0.rank < $1.rank})
print(ranking)

打印:

[(团队:“团队5”,排名:1),(团队:“团队1”,排名:2),(团队:“团队3”, rank: 3), (team: "team2", rank: 4), (team: "team4", rank: 4), (team: "team6", rank: 5)]

好的,它的工作原理,但我确定我错过了一些更好的东西,使用一些排序的闭包,地图襟翼等

有人吗?

【问题讨论】:

  • i &gt; sorted.count 案例没有任何作用;它永远不会被击中。
  • 当然,是个垃圾

标签: ios swift iphone swift5


【解决方案1】:

您可以先将具有相同分数的团队分组到字典中,从而简化它。然后对字典进行排序(根据分数递减),枚举它(得到偏移量)并构建排名列表:

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

let ranking = Dictionary(grouping: dict, by: { $0.value })
    .sorted(by: { $0.key > $1.key })
    .enumerated()
    .flatMap { (offset, elem) in
        elem.value.map { (team: $0.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: { $0.value })

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

.sorted(by: { $0.key > $1.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 调用每个 (offset, element) 对的闭包并连接结果。在封闭内部,

 elem.value.map { (team: $0.key, rank: offset + 1 )}

映射一个 (offset, element) 对和一个 (team, rank) 元组数组。例如,

  (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: { $0.value })
    .sorted(by: { $0.key > $1.key }) {
        let pos = ranking.count + 1
        ranking.append(contentsOf: list.map { ($0.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)]

【讨论】:

  • 您的解决方案看起来不错,但如果有 "team6":1 则会出现问题 --> 打印排名 6 而不是 5
  • @SimonePistecchia:我认为这就是你想要的。 – 但实际上这使它更简单,请参阅更新。
  • @SimonePistecchia 所以如果平局,比分应该是[1, 2, 3, 4, 4, 6] ?
  • 应该是[1, 2, 3, 4, 4, 5]
  • 非常好的解决方案,谢谢。如果你也把前面的解决方案 [1, 2, 3, 4, 4, 6] 也放进去会很有用
【解决方案2】:

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

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

let teamRanks = teamScores
    .sorted(by: { $0.value > $1.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: { $0.value })
    .sorted(by: { $0.key > $1.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: $0.key, rank: rank) }
    }

print(teamRanks)

【讨论】:

  • 请注意,“team2”和“team4”的得分相同 (5),而且——据我从示例输出中了解到——应该具有相同的排名 (4)。
  • 对,“team2”和“team4”的rank应该一样(4)
【解决方案3】:

我认为.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 { $0.value > $1.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 { $0.value > $1.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)

结果是一样的。

【讨论】:

    猜你喜欢
    • 2016-12-30
    • 2016-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多