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

时间:2019-04-04 10:28:10

标签: 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
}

2 个答案:

答案 0 :(得分:0)

f1的计数为0,因此映射不起作用。

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

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

答案 1 :(得分:0)

这里有两个问题,但主要问题是Realm Results正在实时更新;当您可以使用Swifty

过滤数据时
let f1 = activeItems.filter{$0.product != nil}

由于Realm不知道要过滤哪些项目,因此它将给出间歇性结果。.filter {不是Realm函数,Realm不知道在结果中要更新什么。

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

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

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

我相信这个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个房间,并多次运行以上代码而没有崩溃。