Как я могу перемешивать свою структуру с помощью variable priority
и отобразить в TableView?
Итак, теперь у меня есть документы 20 в моей структуре, но позже у меня будут документы 100+ в моей структуре.
5, 7 или 10 документов будут иметь priority
от 10 до 1, другие документы имеют priority
0. Мне нужно отобразить 5, 7 или 10 документов на верхней позиции в tableView. Другие документы, имеющие priority
0, должны располагаться после первых 5, 7 или 10 документов в случайном порядке.
т. е. первые 5 или 7 или 10 документов должны быть размещены в зависимости от приоритета, если документ имеет priority
10, он должен быть первым, следующий, у которого priority
9, должен быть позади документа с priority
10 и так далее до конца. документ с приоритетом 1. Другие документы должны располагаться в случайном порядке.
Этот код, который поможет мне получить документы от firestore
:
fileprivate func observeQuery() {
MBProgressHUD.showAdded(to: self.view, animated: true)
guard let query = query else { return }
let time = DispatchTime.now() + 0.5
listener = query.addSnapshotListener { [unowned self] (snapshot, error) in
if let snapshot = snapshot {
DispatchQueue.main.asyncAfter(deadline: time) {
var photoModels = snapshot.documents.map { (document) -> Photographer in
if let photoModel = Photographer(dictionary: document.data(), id: document.documentID) {
return photoModel
} else {
fatalError("Fatal error")
}
}
self.photographers = photoModels
// this need use shuffle
self.document = snapshot.documents
self.tableView.reloadData()
MBProgressHUD.hide(for: self.view, animated: true)
}
}
}
}
@ielyamani Ненулевые тоже нужно перемешивать. Нет, только один документ имеет приоритет 10, только один документ имеет приоритет 9 и т. д.
Поэтому, если вы собираетесь спросить о сортировке документов в структуре, вам нужно показать определение вашей структуры в своем вопросе. (Покажите все части вашей модели данных, которые имеют отношение к вопросу.)
Что вы могли бы сделать, это
Пример:
var sorted = documents.sorted(by: { $0.priority > $1.priority } )
if let idx = sorted.firstIndex(where: { $0.priority == 0 }) {
sorted[idx...].shuffle()
}
Альтернативой является перетасовка всего массива, а затем выполнение «стабильной сортировки» по уменьшению приоритета, используя идеи из Как стабильно сортировать массив в Swift?:
let sorted = documents.shuffled().enumerated()
.sorted(by: { ($0.element.priority, $0.offset) > ($1.element.priority, $1.offset) })
.map { $0.element }
Это отсортирует записи в порядке убывания и случайным образом перетасует записи все с одинаковым приоритетом, а не только записи с нулевым приоритетом.
Примечание: Методы сортировки из стандартной библиотеки Swift стабильны в Swift 5, поэтому последний подход можно упростить до
let sorted = documents.shuffled()
.sorted(by: { $0.priority > $1.priority } )
Однако это не гарантируется, сравните Является ли sort() стабильным в Swift 5? на форуме Swift.
Хороший трюк с сопоставимыми кортежами! Я должен начать использовать это.
@Sulthan: я узнал об этом от Хэмиша в stackoverflow.com/a/37612765/1187415.
partition быстрее, чем sort для биннинга, поскольку sort на самом деле просто набор разделов. developer.apple.com/documentation/swift/array/3017524-partition
@JoshHomann Это находчивая идея ??! См. тесты ниже. Я включил это в свой ответ, если вы не возражаете
Вы можете поместить документы в ведра:
var buckets = Array(repeating: [Document](), count: 11)
for i in documents.indices {
buckets[10 - documents[i].priority].append(documents[i])
}
Это худший случай алгоритма O(н), в отличие от O(нlog(н) TimSort).
а затем перемешайте последнее ведро:
buckets[10].shuffle()
последнее, но не менее важное: flatMap
массив buckets
:
let sorted = buckets.flatMap { $0 }
Если вы хотите перетасовать еще быстрее, вы можете использовать этот алгоритм модифицированныйФишер-Йейтс (быстрее, чем Array.shuffled()
):
extension Array where Element: Comparable {
mutating func fisherYatesShuffle() {
if count < 2 {
return
}
for i in 1..<count {
let ix = abs(x.next()) % (i+1)
swapAt(i,ix)
}
}
}
Или, в более общем смысле, для MutableCollection
(включая ArraySlice
):
extension MutableCollection where Index == Int, Element: Comparable {
mutating func fisherYatesShuffle() {
if count < 2 {
return
}
let start = self.index(after: startIndex)
for i in start..<endIndex {
let ix = abs(x.next()) % (i + 1 - startIndex) + startIndex
swapAt(i, ix)
}
}
}
Это расширение использует Xoshiro генератор случайных чисел (быстрее, чем Int.random(in:)
, но менее случайно/равномерно):
struct Xoshiro: RandomNumberGenerator {
public typealias StateType = (UInt32, UInt32, UInt32, UInt32)
private var state: StateType
public init(seed: StateType) {
self.state = seed
}
public mutating func next() -> Int {
let x = state.1 &* 5
let result = ((x &<< 7) | (x &>> 25)) &* 9
let t = state.1 &<< 9
state.2 ^= state.0
state.3 ^= state.1
state.1 ^= state.2
state.0 ^= state.3
state.2 ^= t
state.3 = (state.3 &<< 21) | (state.3 &>> 11)
return Int(result)
}
}
var x = Xoshiro(seed: (UInt32.random(in: 0..<10), //Other upper limits could be used to increase randomness
UInt32.random(in: 0..<10),
UInt32.random(in: 0..<10),
UInt32.random(in: 0..<10)))
Чтобы использовать его, Document
должен быть Comparable
:
struct Document: Comparable {
let priority: Int
static func < (lhs: Document, rhs: Document) -> Bool {
return lhs.priority < rhs.priority
}
}
и назовем наше перемешивание так:
buckets[10].fisherYatesShuffle()
Согласно предложению Джош Хоманн, вы также можете разбить массив documents
, поместив документы с нулевым приоритетом после сводного индекса. Затем отсортируйте первый раздел и перетасуйте второй:
var result = documents
let pivot = result.partition { $0.priority == 0 }
result[..<pivot].sort(by: > )
result[pivot...].shuffle()
Здесь - некоторые тесты:
Joakim Danielson's : 1643µs
MartinR's : 169µs
Josh Homann's : 152µs
Orig. Fisher-Yates : 38µs
This : 15µs
(Несмотря на то, что TIO использует Swift 4, относительно сопоставимые результаты были получены с использованием того же кода на моей локальной машине с Swift 5, запущенным в терминале с оптимизацией)
Вот решение, в котором все элементы в массиве сортируются сразу
array.sort(by: {
let first = $0.priority == 0 ? Double.random(in: 0..<1) : Double($0.priority)
let second = $1.priority == 0 ? Double.random(in: 0..<1) : Double($1.priority)
return first > second
})
Если вы хотите избежать приведения к Double, вы можете определить отрицательный диапазон для случайных чисел, я просто жестко закодировал здесь значение, но один из вариантов может заключаться в том, чтобы основывать минимальное значение (-1000) на размере array
array.sort(by: {
let first = $0.priority == 0 ? Int.random(in: -1000..<1) : $0.priority
let second = $1.priority == 0 ? Int.random(in: -1000..<1) : $1.priority
return first > second
})
Должны ли перетасовываться и документы с ненулевым приоритетом? Или сохранить их первоначальный порядок? Могут ли несколько документов иметь одинаковый приоритет? например 3 документа с приоритетом 10?