取消过滤和大数据阵列的排序

时间:2018-02-09 11:21:10

标签: swift sorting filter realm cancellation

我正在使用领域构建词汇应用程序。我有几个词汇对象,其中包含单词列表。一个词汇表包含45000个单词

UI就是这样构建的,用户可以通过" BEGINSWITH"," CONTAINS"或" ENDSWITH"如果选择了相应的标签,则通过单词标题。

因为,有几个词汇表,有一些词汇,出现在几个词汇表中,我需要删除"重复"来自UI。

当我这样做时,对结果对象进行过滤重复,并按字母顺序对应用程序的用户界面进行排序冻结,直到过程完成。

我的问题是: 1)如果更改了选项卡,如何取消先前的过滤器和领域过滤请求(例如从包含到结束"? 2)如何在后台执行所有这些过滤/排序请求,因此UI不会冻结?

我的代码:

    let vocabularyPredicate = NSPredicate(format: "enabled == 1 AND lang_from CONTAINS[c] %@", self.language.value)

    self.vocabularies = Array(realm.objects(Vocabulary.self).filter(vocabularyPredicate).sorted(byKeyPath: "display_order"))

    let result = List<Word>()

    for object in self.vocabularies {
        let predicate = NSPredicate(format: "title \(selectedFilter.value)[c] %@", self.query.value.lowercased())
        result.append(objectsIn: object.words.filter(predicate))
    }

    self.words = Array(result).unique{$0.title}.sorted {
        (s1, s2) -> Bool in return s1.title.localizedStandardCompare(s2.title) == .orderedAscending
    }

selectedFilter.value被选中标签值:&#34; BEGINSWITH&#34;,&#34; CONTAINS&#34;或&#34; ENDSWITH&#34; self.query.value.lowercased() - 搜索查询。

unique {$ 0.title}是数组的扩展方法

extension Array {
    func unique<T:Hashable>(map: ((Element) -> (T)))  -> [Element] {
        var set = Set<T>() //the unique list kept in a Set for fast retrieval
        var arrayOrdered = [Element]() //keeping the unique list of elements but ordered
        for value in self {
            if !set.contains(map(value)) {
                set.insert(map(value))
                arrayOrdered.append(value)
            }
        }

        return arrayOrdered
    }
}

实际上,领域搜索速度非常快,但由于通过词汇表循环并过滤重复项+按字母顺序排序操作数组 - 请求冻结1-2秒。

更新,基于EpicPandaForceManuel建议:

我又潜伏了一次,看来,.distinct(by:[keypath])已经在RealmSwift新版本的结果中显示了。

我已将过滤/排序请求更改为

realm.objects(Word.self).filter(vocabularyPredicate).distinct(by: ["title"]).sorted(byKeyPath: "title", ascending: true) 

更好地了解,但我想确保,通过在后台线程和UI线程之间传递对象,UI无论如何都不会冻结。我已将建议施工更新为:

DispatchQueue.global(qos: .background).async {
        let realm = try! Realm()

        let cachedWords = CashedWords()

        let predicate = NSPredicate(format: "enabled == 1")
        let results = realm.objects(Word.self).filter(predicate).distinct(by: ["title"]).sorted(byKeyPath: "title", ascending: true)

        cachedWords.words.append(objectsIn: results)

        try! realm.write {
            realm.add(cachedWords)
        }

        let wordsRef = ThreadSafeReference(to: cachedWords)

        DispatchQueue.main.async {
            let realm = try! Realm()

            guard let wordsResult = realm.resolve(wordsRef) else {
                return
            }

            self.words = Array(wordsResult.words)

            if ((self.view.window) != nil) {
                self.tableView.reloadData()
            }
        }

        print("data reload finalized")
    }

1 个答案:

答案 0 :(得分:1)

1)如果更改了标签,我如何取消之前的过滤器和域过滤请求(例如从包含到结束??

你可以创建一个NSOperation来执行任务并检查它是否在每个步骤之间被取消(fetch,check isCancelled,filter,check isCancelled,sort)。你不会立即取消它,但它可以提高你的表现。它还取决于这三个步骤中的哪一步(获取,过滤,排序)需要更长的时间......

2)如何在后台执行所有这些过滤/排序请求,因此UI不会冻结?

您可以在新的NSOperationQueue中运行该操作。 或者只使用GCD,将块分派给后台队列,在块中创建Realm实例并在那里运行代码,然后将结果分派回主队列以更新UI。 像这样:

DispatchQueue.global(qos: .userInitiated).async {

    guard let realm = try? Realm() else {
        return // maybe pass an empty array back to the main queue?
    }

    // ...
    // your code here
    // ...

    let words = Array(result).unique{$0.title}.sorted {
        (s1, s2) -> Bool in return s1.title.localizedStandardCompare(s2.title) == .orderedAscending
    }
    // Can't pass Realm objects directly across threads
    let wordReferences = words.map { ThreadSafeReference(to: $0) }

    DispatchQueue.main.async {
        // Resolve references on main thread
        let realm = try! Realm()
        let mainThreadWords = wordReferences.flatMap { realm.resolve($0) }
        // Do something with words
        self.words = mainThreadWords
    }
}

此外,您应该尝试优化查询:

let predicate = NSPredicate(format: "vocabulary.enabled == 1 AND vocabulary.lang_from CONTAINS[c] %@ AND title \(selectedFilter.value)[c] %@", self.language.value, self.query.value.lowercased())
let words = realm.objects(Word.self).filter(predicate).sorted(byKeyPath: "title")
let wordsReference = ThreadSafeReference(words)
// resolve this wordsReference in the main thread