如何通过variable priority
改组结构并将其显示在TableView中?
所以现在我的结构中有 20 个文档,但是以后我的结构中将有 100 + 个文档。
5或7或10个文档的priority
从10到1,其他文档的priority
0。我需要在tableView的顶部位置显示5或7或10个文档。其他priority
为0的文档必须以随机顺序位于前5、7或10个文档之后。
I。 e。应根据优先级放置前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)
}
}
}
}
答案 0 :(得分:4)
你能做的是
示例:
var sorted = documents.sorted(by: { $0.priority > $1.priority } )
if let idx = sorted.firstIndex(where: { $0.priority == 0 }) {
sorted[idx...].shuffle()
}
另一种方法是使用How to stable sort an array in swift?的思想来洗牌整个数组,然后通过降低优先级来进行“稳定排序”:
let sorted = documents.shuffled().enumerated()
.sorted(by: { ($0.element.priority, $0.offset) > ($1.element.priority, $1.offset) })
.map { $0.element }
这将按降序对条目进行排序,并随机洗改具有相同优先级的所有条目,不仅是具有零优先级的条目。
备注: Swift标准库中的sort方法在Swift 5中恰好是稳定的,因此最后一种方法可以简化为
let sorted = documents.shuffled()
.sorted(by: { $0.priority > $1.priority } )
但是,不能保证在Swift论坛中比较Is sort() stable in Swift 5?。
答案 1 :(得分:0)
您可以将文档放入存储桶中:
var buckets = Array(repeating: [Document](), count: 11)
for i in documents.indices {
buckets[10 - documents[i].priority].append(documents[i])
}
这是O( n )算法的最坏情况,不像TimSort的O( n log( n ))。
,然后随机播放最后一个存储桶:
buckets[10].shuffle()
最后但并非最不重要的,flatMap
buckets
数组:
let sorted = buckets.flatMap { $0 }
如果您想更快地进行随机播放,则可以使用以下modified Fisher-Yates算法(比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 random number generator(比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()
按照 Josh Homann 的建议,您还可以对documents
数组进行分区,将文档的优先级设置为零,并在数据透视表索引之后。然后对第一个分区进行排序,然后对第二个分区进行洗牌:
var result = documents
let pivot = result.partition { $0.priority == 0 }
result[..<pivot].sort(by: > )
result[pivot...].shuffle()
Here是一些基准测试:
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在具有优化功能的终端上运行相同的代码来获得相对可比的结果)
答案 2 :(得分:0)
这是一种解决方案,其中数组中的所有元素都可以立即排序
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)基于{{1 }}
array