As of now I'm using:
extension RLMResults {
func toArray () -> [RLMObject] {
var array = [RLMObject]()
for result in self {
array.append(result)
}
return array
}
}
Is there a faster way available to get this conversion? I use it with generic global functions to implicitly cast the RLMResults to the appropriate RLMObject subclass.
public func RLMQuery <T:RLMObject> (_ predicate: String = "TRUEPREDICATE") -> [T] {
return T.objectsWithPredicate(NSPredicate(format: predicate)).toArray() as [T]
}
class Photo {
init (disk: RLMPhoto) {}
}
// first photo in the default realm
let photo = Photo(RLMQuery().first!)
// all photos by the user
let photos: [RLMPhoto] = RLMQuery("creator == \(user.id)")
Thanks! :grin:
There isn't really a faster way to do this, as objects aren't even created until they are requested from an RLMArray during enumeration while creating your swift array.
If you don't explicitly need a swift array, it will in many cases be faster to simply operate off the RLMResults object directly as then objects are only created when actually needed. Is there a reason this approach doesn't work for you?
Thanks for the response! I think I'm just prematurely optimizing by asking this question. Sorry for the hassle.
No hassle at all. Your feedback and questions are greatly appreciated.
I tried to replicate the solution that @aleclarson showed with generics.
func toArray<T>(ofType: T.Type) -> [T] {
var array = [T]()
for result in self {
if let result = result as? T {
array.append(result)
}
}
return array
}
This allows me to do:
let tracks = Track.allObjects().toArray(Track.self) // tracks is of type [Track]
I met the same problem using Swift and found a better way of doing that, without any enumeration at all.
Of course, it can be improved a lot more! It's currently a kind of wrapper for RLMResults, but sure can be used as a pure Swift dynamic collection instead.
My initial problem was the ugly (but necessary) casting when dealing with RLMResults…
extension RLMRealm {
func allObjects<T: RLMObject>(fromClass cls: T.Type) -> RLMSwiftyResults<T> {
return RLMSwiftyResults<T>(results: cls.allObjectsInRealm(self))
}
}
class RLMSwiftyResults<T: RLMObject> {
let results: RLMResults
init(results: RLMResults) {
self.results = results
}
func objectAtIndex(i: UInt) -> T? {
return self.results.objectAtIndex(i) as? T
}
// And all other methods from RLMResults…
}
let mediaItems = RLMRealm.defaultRealm().allObjects(fromClass: MediaItem.self) // mediaItems is now a RLMSwiftyResults<MediaItem>
let firstMediaItem = mediaItem.objectAtIndex(0) // firstMediaItem is now a MediaItem
I also ran into this problem today
Since swift 2.0, we can do this (based on @duemunk's solution):
extension Results {
func toArray() -> [Results.Generator.Element] {
var array = [T]()
for item in self { array.append(item as Results.Generator.Element) }
return array
}
}
do {
let cars = try Realm().objects(Car).toArray() //no need T.Type's paramenter here
}
catch {
}
EDIT: updated to use Array(_ sequence:).
let results: Results<...> // Results
let array = Array(results)
Old answer:
You could just use map:
extension Results {
func toArray() -> [Results.Generator.Element] {
return map { $0 }
}
}
For those with still working with RLMObject in Swift (objc & swift projects), you can put the implementation @duemunk used into one line:
extension RLMResults {
func toArray<T>(ofType: T.Type) -> [T] {
return (0..<self.count).flatMap { self.objectAtIndex($0) as? T }
}
}
Thanks @duemunk!
@chrism21
extension RLMResults {
func toArray<T>(ofType: T.Type) -> [T] {
return flatMap { $0 as? T }
}
}
Even nicer, thanks! @jpsim
Edit:
Actually tried it out but get the compile error:
Cannot convert value of type '(T) -> T? to expected argument type '(_, (_) -> [_])' aka '(_, _ -> Array<_>)')
I'm using Realm for Objective-C, so RLMResults is an Objective-C class. Doesn't that mean it won't have a flapMap to begin with?
Is there a way to do this on JS?
@cmajordev sorry I never saw your last message. RLMResults conforms to SequenceType if you're using RLMSupport.swift as we recommend in our installation instructions: https://realm.io/docs/objc/latest/#installation
flatMap is defined on SequenceType.
@DHidee66 I don't know, this is the cocoa repo. You can file issues towards the realm-js repo for questions like that: https://github.com/realm/realm-js/issues/new
@jpsim just wanted to say thanks for the sweet toArray extension!
@jpsim I guess your nice extension would work with Swift 2.x only?
I get compilation error:
Extension of generic Objective-C class cannot access the class's generic parameters in runtime
I guess this is a limitation.
@tomekc just use the Array() initializer as I mentioned in https://github.com/realm/realm-cocoa/issues/1046#issuecomment-143804257
let results: Results<...> // Results
let array = Array(results)
I did this and it seemed pretty neat.
let results = Results<MyObjectModel>
var dataSource = [MyObjectModel]()
dataSource.append(contentsOf: results)
Again:
let results: Results<MyObjectModel>
let dataSource = Array(results)
But if you're finding yourself copying data out of Realm Collections, you're losing many of their advantages, such as their live updating, lazy-loading, notifications & the ability to pass them across threads via ThreadSafeReference to name a few.
Most helpful comment
@chrism21