Словарь ранжирования Swift

Я могу ранжировать словарь строк и 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", ранг: 3), (команда: "команда2", ранг: 4), ( команда: "team4", ранг: 4), (команда: "team6", ранг: 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", ранг: 3), (команда: "команда2", ранг: 4), ( команда: "team4", ранг: 4), (команда: "team6", ранг: 5)]

хорошо, это работает, но я уверен, что мне не хватает чего-то лучшего, используя закрытие sorted, maps, patchmaps и т. д.

кто угодно?


person Simone Pistecchia    schedule 09.06.2019    source источник
comment
Случай i > sorted.count ничего не делает; он никогда не будет поражен.   -  person Alexander    schedule 09.06.2019
comment
конечно, был отказ   -  person Simone Pistecchia    schedule 09.06.2019


Ответы (2)


Вы можете немного упростить это, сначала сгруппировав команды с одинаковыми баллами в словаре. Затем отсортируйте словарь (по убыванию оценки), пронумеруйте его (чтобы получить смещения) и постройте список ранжирования:

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 вызывает замыкание для каждой пары (смещение, элемент) и объединяет результат. Внутри закрытия,

 elem.value.map { (team: $0.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: { $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)]
person Martin R    schedule 09.06.2019
comment
ваше решение выглядит хорошо, но есть проблема, если есть команда 6: 1 --> печатает ранг 6 вместо 5 - person Simone Pistecchia; 09.06.2019
comment
@SimonePistecchia: Я думал, это то, чего ты хочешь. – Но на самом деле это упрощает задачу, см. обновление. - person Martin R; 09.06.2019
comment
@SimonePistecchia Так что, если есть ничья, счет должен быть что-то вроде [1, 2, 3, 4, 4, 6]? - person Alexander; 09.06.2019
comment
должно быть [1, 2, 3, 4, 4, 5] - person Simone Pistecchia; 09.06.2019
comment
Очень хорошее решение, спасибо. Будет полезно, если вы поставите еще и предыдущее решение [1, 2, 3, 4, 4, 6] - person Simone Pistecchia; 10.06.2019
comment
@SimonePistecchia: я снова добавил это. - person Martin R; 11.06.2019

Да, есть гораздо более простое решение :)

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)
person Alexander    schedule 09.06.2019
comment
Обратите внимание, что team2 и team4 имеют одинаковую оценку (5) и, насколько я понимаю из выходных данных примера, должны иметь одинаковый ранг (4). - person Martin R; 09.06.2019
comment
Верно, team2 и team4 должны иметь одинаковый ранг (4) - person Simone Pistecchia; 09.06.2019