swift - Filter Realm Results 数组返回应过滤的元素

标签 swift realm

您知道为什么此过滤器无法正常工作吗?

for item in activeItems {
        print("item.product: \(item.product), \(item.spaceRequired)")
    }

返回

item.product: nil, 40.0

过滤产品为零的地方

let f1 = activeItems.filter{$0.product != nil}
print("f1: \(f1)")
print("f1.count: \(f1.count)")

返回计数为零,但数组似乎仍然包含一个项目

f1: LazyFilterSequence<Results<AssortmentItem>>(_base: Results<AssortmentItem> <0x109ce2c90> (
    [0] AssortmentItem {
...

f1.count: 0

然后过滤并映射 spaceRequired

let f11 = f1.filter{$0.product!.isProduct == true}.map({$0.spaceRequired})
    print("f11: \(f11)")

返回具有单个项目的相同数组

f11: LazyMapSequence<LazyFilterSequence<Results<AssortmentItem>>, Double>(_base: Swift.LazyFilterSequence<RealmSwift.Results<Merchandise_Manager.AssortmentItem>>(_base: Results<AssortmentItem> <0x109ce2c90> (
    [0] AssortmentItem {

然后尝试减少崩溃

let w = f11.reduce(0,+)

这似乎可以解决问题

let width = Array(activeItems.filter{$0.product != nil}).filter{$0.product!.isProduct == true}.map({$0.spaceRequired}).reduce(0,+)

这是 Swift 5 或 Realm 中的错误吗?

编辑:看起来这是 Realm 处理事物时的一个错误。

下面需要澄清的是一组更完整的 Realm 对象。

import Foundation
import RealmSwift

let activeDate: NSDate      = Date() as NSDate
let defaultWidth: Double    = 40.0

class MyObject: Object {
    @objc dynamic var number: Int = 0
    @objc dynamic var name: String?       

    let items = List<ChildObject>()
}

extension MyObject {

    var activeItems: Results<ChildObject> {
        let activeDate = activeDate                  // Some globally defined value
        let active = items.filter("startDate <= %@ && (endDate >= %@ || endDate == nil)", activeDate, activeDate).sorted(byKeyPath: "number")
        return active
    }

    /// Works Correctly
    var totalWidth: Double {

        let width = Array(activeItems.filter{$0.product != nil}).filter{$0.product!.isProduct == true}.map({$0.spaceRequired}).reduce(0,+)
        let width2 = Array(activeItems.filter{$0.product == nil}.map({$0.spaceRequired})).reduce(0,+)

        return width+width2
    }

    /// Crashes
    var totalWidth: Double {

        let width = activeItems.filter{$0.product != nil}.filter{$0.product!.isProduct == true}.map({$0.spaceRequired}).reduce(0,+)
        let width2 = activeItems.filter{$0.product == nil}.map({$0.spaceRequired}).reduce(0,+)

        return width+width2
    }
}

class ChildObject: Object {
    @objc dynamic var parent: MyObject?
    @objc dynamic var number: Int = 0
    @objc dynamic var product: Product?   
    @objc dynamic var name: String?    
    @objc dynamic var spaceRequired: Double = 40.0
    @objc dynamic var startDate: NSDate?
    @objc dynamic var endDate: NSDate?
}

extension ChildObject {
    var spaceRequired: Double {
        if let p = product {
            return p.width
        } else {
            return defaultWidth
        }
    }
}

class Product: Object {
    @objc dynamic var isProduct: Bool = false
    @objc dynamic var width: Double = 30.0
}

最佳答案

这里存在一些问题,但主要问题是 Realm 结果是实时更新的;而您可以使用 Swifty 过滤数据

let f1 = activeItems.filter{$0.product != nil}

它将给出间歇性的结果,因为 Realm 不知道哪些项目被过滤或未被过滤,因为 .filter { 不是 Realm 函数,并且 Realm 不知道在结果中更新什么。

您通常应该使用内置的 Realm 过滤机制

let results = realm.objects(ItemClass.self).filter("product != nil")

这些结果将实时更新 - 如果对象离开过滤器参数,结果将随之而来。如果对象与过滤器匹配,结果也会更新。

我相信这个Github issue #2138提供了有关该问题的更多信息。

如果您绝对需要静态数据,那么我建议扩展 Results 类以返回数组;像这样

extension Results {
    func toArray() -> [Element] {
        return compactMap { $0 }
    }
}

请记住,这将使用更多内存,因为 Realm 对象是延迟加载的,而数组则不是。

编辑:

问题中有一些附加信息,因此我制作了一个简单的示例来尝试复制该问题。有一个 HouseClass 对象,其中包含 RoomClass 对象列表,然后扩展 HouseClass 以返回其列表中所有房间的总宽度。

class RoomClass: Object {
    @objc dynamic var room_name = ""
    @objc dynamic var width = 0
    @objc dynamic var length = 0
    @objc dynamic var belongs_to_house: HouseClass!
}

class HouseClass: Object {
    @objc dynamic var house_id = NSUUID().uuidString
    @objc dynamic var house_name = ""
    let rooms = List<RoomClass>()

    override static func primaryKey() -> String? {
        return "house_id"
    }
}

extension HouseClass {
    var totalWidth: Int {
        let width = Array(rooms).map {$0.width}.reduce(0,+)
        return width
    }

    var anotherTotalWidth: Int {
        let width = rooms.map {$0.width}.reduce(0,+)
        return width
    }
}

然后是获取所有房屋并根据两个不同函数输出其房间宽度的代码(请参阅 HouseClass 扩展)

let houseResults = realm.objects(HouseClass.self)

for house in houseResults {
    let w0 = house.totalWidth
    print(w0)

    let w1 = house.anotherTotalWidth
    print(w1)
}

我添加了 100 个房屋,每个房屋有 3 个房间,并多次运行上述代码,没有崩溃。

关于swift - Filter Realm Results 数组返回应过滤的元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55513645/

相关文章:

ios - RealmSwift - librealm-ios.a 占用太多空间

swift - 我们如何在 swift 中删除除数字、点和冒号以外的所有字符?

arrays - 有没有更好的方法来存储地牢生成器中房间的数据?

ios - 快速恢复 AVAudioPlayer 中的声音

ios - prepareforsegue() 未执行(swift/storyboards)

android - Realm 突然开始在正常工作的代码中生成编译错误 _ClassNameRealmProxy

swift - 在 Swift 脚本中使用 CleanroomLogger 无法解析 DefaultLogConfiguration

swift - RxSwift + RxRealm + RxCocoa 向 UITableView 插入行

ios - 尽管尝试了官方 SO 答案的所有方法,但我找不到我的 .realm 文件

java - realm是关系型数据库吗?