Перемешать структуру Int

Как я могу перемешивать свою структуру с помощью 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)
            }
        }
    }
}

Должны ли перетасовываться и документы с ненулевым приоритетом? Или сохранить их первоначальный порядок? Могут ли несколько документов иметь одинаковый приоритет? например 3 документа с приоритетом 10?

ielyamani 06.04.2019 14:55

@ielyamani Ненулевые тоже нужно перемешивать. Нет, только один документ имеет приоритет 10, только один документ имеет приоритет 9 и т. д.

Дмитрий Деникаев 06.04.2019 15:09

Поэтому, если вы собираетесь спросить о сортировке документов в структуре, вам нужно показать определение вашей структуры в своем вопросе. (Покажите все части вашей модели данных, которые имеют отношение к вопросу.)

Duncan C 06.04.2019 15:43
Структурированный массив Numpy
Структурированный массив Numpy
Однако в реальных проектах я чаще всего имею дело со списками, состоящими из нескольких типов данных. Как мы можем использовать массивы numpy, чтобы...
T - 1Bits: Генерация последовательного массива
T - 1Bits: Генерация последовательного массива
По мере того, как мы пишем все больше кода, мы привыкаем к определенным способам действий. То тут, то там мы находим код, который заставляет нас...
Что такое деструктуризация массива в JavaScript?
Что такое деструктуризация массива в JavaScript?
Деструктуризация позволяет распаковывать значения из массивов и добавлять их в отдельные переменные.
0
3
197
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Что вы могли бы сделать, это

  • Сначала отсортируйте документы по убыванию приоритета.
  • Затем перемешайте часть массива с документами с нулевым приоритетом.

Пример:

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 06.04.2019 17:29

@Sulthan: я узнал об этом от Хэмиша в stackoverflow.com/a/37612765/1187415.

Martin R 06.04.2019 17:32

partition быстрее, чем sort для биннинга, поскольку sort на самом деле просто набор разделов. developer.apple.com/documentation/swift/array/3017524-partit‌​ion

Josh Homann 07.04.2019 01:01

@JoshHomann Это находчивая идея ??! См. тесты ниже. Я включил это в свой ответ, если вы не возражаете

ielyamani 07.04.2019 12:09

Вы можете поместить документы в ведра:

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 }

Скоростной перемешивание

Muhammad Ali

Если вы хотите перетасовать еще быстрее, вы можете использовать этот алгоритм модифицированныйФишер-Йейтс (быстрее, чем 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
})

Другие вопросы по теме