How to get min/max Values of Realms LinkingObjects

Viewed 292

What is the most efficient way to get the overall max speed over all Customers/Cars.

// 157087 Entries (already filtered)
class Customer: Object {
    //...
    let cars = LinkingObjects(fromType: Car.self, property: "customer")
}

// 2537950 Entries for the 157087 filtered Customers
class Car: Object {
    //...
    @objc dynamic var customer: Customer?
}

// results here are already filtered (For simplicity i kept the additional filters away)
var results : Results<Customer> = realm.filter("age > 18")
// takes several seconds
let maxSpeed = results.map { $0.cars.max(ofProperty: "speed") as Double? }.max() ?? 0

is there a better way to do so? Just for benchmarking I tried the other way around. It takes just as long

// need to add a `IN` clause because of the pre filtered results (see above)
let cars : Results<Car> = realm.objects(Car.self).filter("customer IN %@", results)
let maxSpeed = cars.max(ofProperty: "speed") as Double?
1 Answers

Super question; clear, concise and property formatted.

I don't have a 2 million row dataset but a few things that may/may not be a complete answer:

1)

This could be bad

results.map { $0.cars.max(ofProperty: "speed") as Double? }.max()

Realm objects are lazily loaded so as long as you work with them in a Realm-ish way, it won't have a significant memory impact. However, as soon as Swift filters, maps, reduce etc. are used ALL of the objects are loaded into memory and could overwhelm the device. Considering the size of your dataset, I would avoid this option.

2)

This is a good strategy as shown in your question as it it treats it as Realm objects, memory won't be affected and is probably your best solution

let max: Double = realm.objects(Car.self).max(ofProperty: "speed") ?? 0.0 //safely handle optional
print(max)

3)

One option we've used in some use cases is ordering the object and then grabbing the last one. Again, it's safer and we generally do this on a background thread to not affect the UI

if let lastOne = realm.objects(Car.self).sorted(byKeyPath: "speed").last {
    print(lastOne.speed)
}

4)

One other thought: add a speed_range property to the object and when it's initially written update that as well.

class Car: Object {
   @objc dynamic var speed = 0.0
   @objc dynamic var speed_range = 0
}

where speed_range is:

0:  0-49
1:  50-99
2:  100-149
3:  150-199
4:  200-249
etc

That would enable you to quickly narrow the results which would dramatically improve the filter speed. Here we want the fastest car in the speed_range of 4 (200-249)

let max: Double = realm.objects(Car.self).filter("speed_range == 4").max(ofProperty: "speed") ?? 0.0
print(max)

You could add a calculated property backed Realm property to the Car object that automatically set's it to the correct speed_range when the speed is initally set.

Related