代码之家  ›  专栏  ›  技术社区  ›  Duncan Groenewald

Filter Realm Results数组返回应筛选的元素

  •  0
  • Duncan Groenewald  · 技术社区  · 5 年前

    你知道为什么这个过滤器不能正常工作吗?

    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
    

    然后过滤和映射

    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中的bug还是领域中的bug?

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

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

    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
    }
    
    0 回复  |  直到 5 年前
        1
  •  1
  •   Jay    5 年前

    这里有几个问题,但主要问题是领域结果是实时更新;而你 可以 使用Swifty筛选数据

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

    由于Realm不知道哪些项目被过滤或不被过滤,所以它会给出间歇的结果。filter{不是领域函数,领域不知道在结果中更新什么。

    通常应使用内置的领域过滤机制

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

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

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

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

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

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

    编辑:

    这个问题中有一些额外的信息,所以我制作了一个简单的示例,试图复制这个问题。有一个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个房子,每个房子有三个房间,并多次运行上述代码,没有崩溃。

        2
  •  0
  •   Yervand Saribekyan    5 年前

    的计数 f1 0 所以地图不起作用。

    您可以按以下方式优化宽度计算

    let width = activeItems
          .filter { $0.product?.isProduct ?? false }
          .map { $0.spaceRequired }
          .reduce(0,+)
    
    推荐文章