I was wondering if there is a possibility in realm.io (swift) to select all items from one "table" that are not in the other one.
Lets say you have 2 classes:
class A: Object {
dynamic var id: Int = 0
dynamic var text: String = ""
}
class B: Object {
dynamic var id: Int = 0
dynamic var value: Bool = false
}
Is it possible to get an result of items from A who's id is not present in B?
There is actually a very simple way to do this using NSPredicate on Realm filter API.
func fetch() throws -> [A] {
do {
// Create Realm
let realm = try Realm()
// Get B objects from Realm and put their IDs to [Int] array
let IdB: [Int] = realm.objects(B).map { $0.id }
// Create predicate
// Filter all items where property id is not present in array IdB
let predicateFilter = NSPredicate(format: "NOT (id IN %#)", IdB)
// Get all A objects from array using predicateFilter
let objectsA = realm.objects(A).filter(predicateFilter)
// Return the [A] array
return objectsA.map { $0 }
} catch {
// Throw an error if any
throw error
}
}
Also note that all objects from fetched using Realm are lazy loaded which means that this method is also very fast. From the documentation:
All queries (including queries and property access) are lazy in Realm. Data is only read when the properties are accessed.
Related
I have the following 2 class / structs:
class ConversationDetails {
var messages: [ChatMessage]?
var participants: [User]?
}
class User: Codable {
init (email: String) {
self.email = email
}
// system
var id: String?
var verifiedaccount: Int?
var rejected: Int?
...
}
I've further got the var conversationDetails = ConversationDetails () and I'm populating it with an API call. That all works fine.
I'd like to map the participants array inconversationDetailsand access the id property of each participant like so:
let recipient_ids = self.conversationDetails.participants.map( { (participant) -> String in
return participant.id
})
In my understanding, map iterates over the entire participants array, which is an array of User objects and I can access each item via participant.
However, I get Value of type '[User]' has no member 'id' for return participant.id.
Where is my misunderstanding?
Your participants var is optional, so you need to add question mark to access array. Without it, you try to call map on optional.
This is working code:
let recipientIds = conversationDetails.participants?.map( { (participant) -> String in
return participant.id
})
or shorter:
let recipientIds = conversationDetails.participants?.map { $0.id }
Also, you can use compactMap to remove nils from recipientIds array and have [String] array instead of [String?]:
let recipientIds = conversationDetails.participants?.compactMap { $0.id }
The first problem is that participants is optional so you need to add a ? when accessing it
conversationDetails.participants?
then when mapping you should use compactMap since id is also an optional property
let recipient_ids = conversationDetails.participants?.compactMap { $0.id }
Another variant is to not have an optional array but instead initialize it to an empty array. This is actually a much better way to handle collection properties because you can have a cleaner code by initializing them to an empty collection
var participants = [User]()
and then do
let recipient_ids = conversationDetails.participants.compactMap { $0.id }
I'm trying to query multiple objects from Realm using a List of Primary Key Strings. I know I can do this using a for..in loop but I'd rather use a filter if possible.
primaryKeyArray contains a number of Strings
class Item : Object {
#objc dynamic var itemKey = NSUUID().uuidString
}
var primaryKeyArray : List<String>?
//Assume Realm DB already contains multiple Item Objects
//primaryKeyArray contains "key1", "key2", "key3", etc..
let predicate = NSPredicate(format: "itemKey == %#", primaryKeyArray)
let items = realm.objects(Item.self).filter(predicate)
I know the problem is with my predicate format. Not sure whether to use some form of CONTAINS or what? Any help with the predicate syntax would be greatly appreciated!
I think you are asking how to query Realm for items that have keys that match a set of keys in an array.
So given a DogClass Realm Object
class DogClass: Object {
#objc dynamic var dog_id = NSUUID().uuidString
#objc dynamic var dog_name = ""
override static func primaryKey() -> String? {
return "dog_id"
}
}
and suppose we know we want to retrieve three dogs that match some given primary keys
let keysToMatch = ["302AC133-3980-41F3-95E8-D3E7F639B769", "54ECC485-4910-44E5-98B9-0712BB99783E", "71FE403B-30CD-4E6C-B88A-D6FDBB08C509"]
let dogResults = realm.objects(DogClass.self).filter("dog_id IN %#", keysToMatch)
for dog in dogResults {
print(dog.dog_id, dog.dog_name)
}
Note the use of IN in the filter, which will match any dogs with id's in the given array.
You can also pass in a Realm List Object instead of a Swift array and get the same result.
let listOfKeysToMatch = List<String>()
listOfKeysToMatch.append("302AC133-3980-41F3-95E8-D3E7F639B769")
listOfKeysToMatch.append("54ECC485-4910-44E5-98B9-0712BB99783E")
listOfKeysToMatch.append("71FE403B-30CD-4E6C-B88A-D6FDBB08C509")
let dogResults2 = realm.objects(DogClass.self).filter("dog_id in %#", listOfKeysToMatch)
for dog in dogResults2 {
print(dog.dog_id, dog.dog_name)
}
let predicate = NSPredicate(format: "itemKey IN %#", primaryKeyArray)
I have the following core data model:
where Person to Codes is a one-to-many relationship.
I have a function which returns a Person record and if the code person.codes returns an NSSet of all the codes associated with that Person. The issue that I am having is how to use the NSSet.
person.codes.allObjects.first returns this data:
<Codes: 0x60000213cb40> (entity: Codes; id: 0xb978dbf34ddb849 <x-coredata://A2B634E4-E136-48E1-B2C5-82B6B68FBE44/Codes/p1> ; data: {
code = 4LQ;
number = 1;
whosAccount = "0xb978dbf34ddb869 <x-coredata://A2B634E4-E136-48E1-B2C5-82B6B68FBE44/Person/p1>";
})
I thought if I made person.codes.allObjects.first of type Codes, I would be able to access the code and number elements but I get an error: error: value of type 'Any?' has no member 'number'
Also, how can I search this data set for a particular code or number.
I appreciate that this is proabably a simple question but have searched and read the documentation to no avail. I suspect that may base knowledge is not sufficient.
Update
I have a CoreDataHandler class which contains the following code:
class CoreDataHandler: NSObject {
//static let sharedInstance = CoreDataHandler()
private static func getContext() -> NSManagedObjectContext {
let appDelegate = NSApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
static func fetchPerson() -> [Person]? {
let context = getContext()
do {
let persons: [Person] = try context.fetch(Person.fetchRequest())
return persons
} catch {
return nil
}
}
I can fetch a person using:
let row = personTableView.selectedRow
let person = CoreDataHandler.fetchPerson()?[row]
Core Data supports widely native Swift types.
Declare codes as Set<Codes> in the Person class.
It's much more convenient than typeless NSSet.
You get a strong type and you can apply all native functions like filter, sort, etc. without type cast.
let codes = person.codes as! Set<Code>
Once that is done you can access the properties. Searching can be done by filtering for instance
let filteredCodes = codes.filter({ $0.code == "XYZ" })
will return all objects that has the code "XYZ". Or to get only one you can use
let code = codes.first(where: {$0.id == 1})
which will return the first object that has id = 1
A simple example getting all Person objects that has a given code
func findWithCode(_ code: String) -> [Person] {
guard let persons = CoreDataHandler.fetchPerson() else {
return []
}
var result = [Person]()
for person in persons {
let codes = person.codes as! Set<Code>
if codes.contains(where: { $0.code == code }) {
result.append(person)
}
}
return persons
}
My realm model class look like
class RoomRealmModel : Object {
dynamic var id: String = ""
var details = List<RoomDetailRealmModel>()
func saveItem() {
do {
let realm = try Realm()
realm.beginWrite()
realm.add(self, update: true)
try realm.commitWrite()
} catch{}
}
}
class RoomDetailRealmModel : Object{
dynamic var detailId: String = ""
dynamic var displayText: String = ""
}
I want to retrieve 'details' from the following.
details = RLMArray<RoomDetailRealmModel> <0x600000114f40> (
[0] RoomDetailRealmModel {
text = hello;
Counters = 9;
ParentID = ;
detailId = 33;
displayText = hello ;
}
);
I always get empty like in my console
(lldb) po (destinationData?[index]?.details)!
List<RoomDetailRealmModel> <0x600000853620> (
)
I am updating ‘details’ list via realm update command. I always get realm array.But I want to retrieve array type from realm array.Please help me, how to solve this issue
If you want to obtain [myObject] instead of List you can do something like this:
var array: [myObject] = [myObject]()
for object in myObjectList {
array.append(object)
}
Where myObjectList is List.
You can simply create a regular Swift Array from a Realm List by calling the initializer of Array accepting a Sequence, since List conforms to the sequence protocol.
So you can simply do
let room = RoomRealmModel()
let roomDetailsArray = Array(room.details)
I always get the following error when trying to filter my Realm database using NSPredicate:
Property 'text' is not a link in object of type 'getType'
I want to filter my Realm database to show only the items that have some specific text in them. This is what I've tried:
let realm = try! Realm()
let predicate = NSPredicate(format: "typez.text.filter = 'special'")
let filterThis = realm.objects(Publication).filter(predicate)
print(filterThis)
The relevant portion of my model classes is:
class Publication: Object, Mappable {
dynamic var id: Int = 0
var typez = List<getType>()
dynamic var url: String?
}
class getType: Object, Mappable {
dynamic var text: String = ""
}
You mentioned that the relevant portions of you model classes look like so:
class Publication: Object, Mappable {
dynamic var id: Int = 0
var typez = List<getType>()
dynamic var url: String?
}
class getType: Object, Mappable {
dynamic var text: String = ""
}
If I understand you correctly, you want to find Publication instances that have an entry in their typez list with text equal to special. You can express that as:
let realm = try! Realm()
let result = realm.objects(Publication).filter("ANY typez.text = 'special'")
print(result)
I was not liking the accepted answer here because it doesn't actually answer the question... but then it helped me more than I realized. I will now be using closures instead of NSPredicates whenever possible. The actual answer to this question should be a slightly modified version of #NSGangster's answer:
let realm = try! Realm()
//Array of publications
let realmObjects = realm.objects(Publication)
//any publication where .text property == special will be filtered. and filter out empty array
let filterThis = realmObjects.filter({ $0.typez.filter({ $0.text == "special" } != [] ) })
print(filterThis)
.. or something close to that.
But what I was looking for was a bit different. I needed a way to filter on exact words of a multi-word string, and using an NSPredicate with "CONTAINS" would match any containing substring, e.g. a search for "red" would match "fred". Realm doesn't support "LIKE" or regex yet, so using a closure was the only thing I could get to work:
//I was going for a "related terms" result for a dictionary app
let theResults = terms.filter(
{
//Looking for other terms in my collection that contained the
//title of the current term in their definition or more_info strings
$0.definition.components(separatedBy: " ").contains(term.title) ||
$0.more_info.components(separatedBy: " ").contains(term.title)
}
)
With as much of the day as I spent searching, hopefully this helps someone else with a similar issue.
I don't usually use NSPredicate's directly, instead I do an inline predicate closure within the filter paramter.
let realm = try! Realm()
//Array of publications
let realmObjects = realm.objects(Publication)
//any publication where .text property == special will be filtered. and filter out empty array
let filterThis = realmObjects.filter({ $0.getType.filter({ $0.text == "special" } != [] ) })
print(filterThis)