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)
Related
I get the error *** Terminating app due to uncaught exception 'Invalid property name', reason: 'Property 'read' not found in object of type 'Book'' when I want to filter my data. My structure:
class Book: Object, Codable {
#objc dynamic var author = ""
#objc dynamic var title = ""
#objc dynamic var imageLink = ""
#objc dynamic var category = "Lk"
#objc dynamic var date = Date()
convenience init(withBookDict: [String: Any]) {
self.init()
self.author = withBookDict["author"] as? String ?? "No Author"
self.title = withBookDict["title"] as? String ?? "No Title"
self.imageLink = withBookDict["imageLink"] as? String ?? "No link"
self.category = withBookDict["category"] as? String ?? "No category"
}
}
my code for filtering data is this:
let filteredread = realm.objects(Book.self).filter({ $0.category == "read"})
but I also tried this:
let filteredread = realm.objects(Book.self).filter("category == 'read'")
also I did update my realm pod since there have been version issues.
There's actually nothing wrong with your code, other than a "typo" - but there's other stuff to consider.
Fixing the typo
let filteredread = realm.objects(Book.self).filter({ $0.category == "read"})
should be
let filteredread = realm.objects(Book.self).filter { $0.category == "read"}
note removing the () from the filter function (which is actually a Swift .filter). This is more for clarity than anything as using swift functions on Realm objects can cause issues.
That query will first read all of the Book objects, then perform a Swift filter on the results and return LazyFilterSequence<Results> object. What that means is the results are disconnected from Realm and will not auto-update, which is one of the key features of realm.
If you want to have the Results update, do not use Swift functions; use Realm functions only
let filteredread = realm.objects(Book.self).filter("category == 'read'")
which returns a Results, which will auto-update as the underlying data changes.
Note: that function matches what's in your question and is perfectly legitimate code and would not throw any errors.
The way to filter Realm results according to a string property is :
let filteredResult = realm.objects(Model.self).filter("propertyName == %#", "value")
In your case it would be:
let filteredread = realm.objects(Book.self).filter("category == %#", "read")
I'm looking to filter an array of arrays by specific value of one of the keys located within each array. Each nested array is read in from Firestore.
As an object, each nested array would look like this:
struct Video {
var url: String
var submissionDate: Timestamp
var submittingUser: String
}
I'm reading it in like this:
videos = document.get("videos") as? [[String : Any]] ?? nil
So far so good, but when I filter it like this:
filteredVideos = videos.filter { $0[2].contains(self.userIdentification) }
I can't do it without getting the error "Reference to member 'contains' cannot be resolved without a contextual type," an error which I was unable to find any relevant information on SO about.
I have read that some people say "Don't use arrays in Firestore!" but this is a build requirement.
Anyone have any ideas? Basically just need all arrays within the array where userId == submittingUser.
Reference Article:
I tried the answer from here: How to filter out a Array of Array's but no luck for this situation.
It's actually an array of dictionaries, not an array of arrays. All you need to do is construct the right predicate for the filter.
This is basically what your Firestore data looks like:
let videos: [[String: Any]] = [
["url": "http://www...", "submittingUser": "user1"],
["url": "http://www...", "submittingUser": "user2"]
]
This is the user you're looking for:
let userIdentification = "user2"
This is the predicate for the filer:
let filteredVideos = videos.filter { (video) -> Bool in
if let submittingUser = video["submittingUser"] as? String,
submittingUser == userIdentification {
return true
} else {
return false
}
}
You can shorthand this down to a single line if you're okay with force-unwrapping the dictionary (if you're 100% certain every video will have a valid submittingUser value):
let filteredVideos = videos.filter({ $0["submittingUser"] as! String == userIdentification })
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)
Ok, I am working in an iMessage app and am trying to parse more than 1 url query item from the selected message here- I have been successful getting/sending just 1 value in a query:
override func willBecomeActive(with conversation: MSConversation) {
// Called when the extension is about to move from the inactive to active state.
// This will happen when the extension is about to present UI.
if(conversation.selectedMessage?.url != nil) //trying to catch error
{
let components = URLComponents(string: (conversation.selectedMessage?.url?.query?.description)!)
//let val = conversation.selectedMessage?.url?.query?.description
if let queryItems = components?.queryItems {
// process the query items here...
let param1 = queryItems.filter({$0.name == "theirScore"}).first
print("***************=> GOT IT ",param1?.value)
}
}
When I just have 1 value, just by printing conversation.selectedMessage?.url?.query?.description I get an optional with that 1 value, which is good. But with multiple I cant find a clean way to get specific values by key.
What is the correct way to parse a URLQueryItem for given keys for iMessage?
When you do conversation.selectedMessage?.url?.query?.description it simply prints out the contents of the query. If you have multiple items then it would appear something like:
item=Item1&part=Part1&story=Story1
You can parse that one manually by splitting the string on "&" and then splitting the contents of the resulting array on "=" to get the individual key value pairs in to a dictionary. Then, you can directly refer to each value by key to get the specific values, something like this:
var dic = [String:String]()
if let txt = url?.query {
let arr = txt.components(separatedBy:"&")
for item in arr {
let arr2 = item.components(separatedBy:"=")
let key = arr2[0]
let val = arr2[1]
dic[key] = val
}
}
print(dic)
The above gives you an easy way to access the values by key. However, that is a bit more verbose. The way you provided in your code, using a filter on the queryItems array, is the more compact solution :) So you already have the easier/compact solution, but if this approach makes better sense to you personally, you can always go this route ...
Also, if the issue is that you have to write the same filtering code multiple times to get a value from the queryItems array, then you can always have a helper method which takes two parameters, the queryItems array and a String parameter (the key) and returns an optional String value (the value matching the key) along the following lines:
func valueFrom(queryItems:[URLQueryItem], key:String) -> String? {
return queryItems.filter({$0.name == key}).first?.value
}
Then your above code would look like:
if let queryItems = components?.queryItems {
// process the query items here...
let param1 = valueFrom(queryItems:queryItems, key:"item")
print("***************=> GOT IT ", param1)
}
You can use iMessageDataKit library. It makes setting and getting data really easy and straightforward like:
let message: MSMessage = MSMessage()
message.md.set(value: 7, forKey: "user_id")
message.md.set(value: "john", forKey: "username")
message.md.set(values: ["joy", "smile"], forKey: "tags")
print(message.md.integer(forKey: "user_id")!)
print(message.md.string(forKey: "username")!)
print(message.md.values(forKey: "tags")!)
(Disclaimer: I'm the author of iMessageDataKit)
I do news application in swift using Realm database. In my database have same news categories. How to get unique value from Realm database?
I use primary key
class News: Object {
dynamic var newsID: String = ""
dynamic var newsTitle: String = ""
dynamic var newsFullText: String = ""
dynamic var newsImage: String = ""
dynamic var newsAutor: String = ""
dynamic var newsCommentCount: String = ""
dynamic var newsSeenCount: String = ""
dynamic var newsDate: String = ""
dynamic var newsCategory: String = ""
override static func primaryKey() -> String? {
return "newsID"
}
}
I'm try to get
let realm = try! Realm()
let menuName = realm.objects(News)
for i in menuName.filter("newsCategory") {
nameLabel.text = i.newsCategory
}
But it is not work.
Starting from Realm 3.10 it's now possible to
Add Results.distinct(by:) / -[RLMResults
distinctResultsUsingKeyPaths:], which return a Results containing only
objects with unique values at the given key paths.
Old response - before Realm 3.10
It is not possible yet to obtain a "distinct"-like functonality from a Realm query (track the open issue here)
However, there are some workarounds suggested in the thread I mentioned above (please read it to get the full context), by user apocolipse :
// Query all users
let allUsers = Realm().objects(User)
// Map out the user types
let allTypes = map(allUsers) { $0.type }
// Fun part: start with empty array [], add in element to reduced array if its not already in, else add empty array
let distinctTypes = reduce(allTypes, []) { $0 + (!contains($0, $1) ? [$1] : [] )
Or better yet, a different approach using Sets (by user jpsim):
let distinctTypes = Set(Realm().objects(User).valueForKey("type") as! [String])
Obviously the workarounds aren't as efficient as a direct DB query, so use with care (and testing under realistic load).