A reverse kind of string compare using NSPredicate - objective-c

I've been searching for this answer all over internet but so far no luck. So I need to consult the smart and nice people here. This is my first time asking a question here, so I hope I am doing this right and not repeating the question.
For all the examples I saw, it's the search string that is a substring of what's stored in the Core Data. On the other hand, I want to achieve the following:
The strings stored in core data are actually sub-strings. I want to do a search by getting all core data rows that have substrings belong to the provided search string.
For ex:
In core data, I have "AB", "BC","ABC","ABCDEF","GH", "ABA"
And in the app I do a search by providing the super-string: "ABCDEF", the result will return "AB","BC","ABC","ABCDEF" but not "GH", "ABA" because these two sub-strings don't belong to the super-string.
How should I setup my predicateWithFormat statement?
This wont' work cuz it's doing the opposite:
NSPredicate *myPredicate = [NSPredicate predicateWithFormat:#"substring LIKE[c] %#", #"ABCDEF"];
Thanks all!

The reverse of CONTAINS will not work. Also, you will not be able to use LIKE because you would have to take the attribute you are searching and transform it into a wildcard string.
The way to go is to use MATCHES because you can use regular expressions. First, transform your search string into a regex by affixing a * after each letter. Then form the predicate.
This solution has been tested to work with your example.
NSString *string= #"ABCDEF";
NSMutableString *new = [NSMutableString string];
for (int i=0; i<string.length; i++) {
[new appendFormat:#"%c*", [string characterAtIndex:i]];
}
// new is now #"A*B*C*D*E*F*";
fetchRequest.predicate = [NSPredicate predicateWithFormat:
#"stringAttribute matches %#", new];
where stringAttribute in the predicate is the name of your NSString attribute of your managed object.

I think this will work:
NSPredicate *pred = [NSPredicate predicateWithFormat:#"%# contains self",#"ABCDEF"];
You would use it like this in core data:
-(IBAction)doFetch:(id)sender {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
request.entity = [NSEntityDescription entityForName:#"Expense" inManagedObjectContext:self.managedObjectContext];
request.predicate = [NSPredicate predicateWithFormat:#"%# contains desc",#"ABCDEF"];
NSArray *answer = [self.managedObjectContext executeFetchRequest:request error:nil];
NSLog(#"%#",answer);
}
In this example, "desc" is an attribute of the entity "Expense". This correctly retrieves only the rows where "desc" is a substring of "ABCDEF".

Related

filtering JSON stings array with NSPredicate and regular expression

I have an NSArray containing JSON string like below.
[
{
"title":"find good book",
"isCompleted":true
},
{
"title":"complete work",
"isCompleted":false
},
{
"title":"check schedule",
"isCompleted":false
}
]
I want to find indexes of objects having "plet" value under "title" key. When I just simply used below NSPredicate, it returned every objects, because every JSON objects having "isCompleted" stings as a key.
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"self CONTAINS[cd] %#", #"plet"];
I don't want to convert JSON to NSDictionary object. And I don't use [NSPredicate predicateWithBlock:], because I am going to apply this predicate to NSFetchedResultsController. According to document, NSPredicate which is created by [NSPredicate predicateWithBlock:] can't be used to NSFetchedResultsController.
So my question is, can I use Regular expression to solve this problem? I don't know much about regular expression. Could anyone give example for this?
You can use predicateWithBlock method for creating your predicate. Inside the block, check whether the dynamicVale contains the value to be searched.
Code:
NSArray *yourArray = ....;
NSString *searchText = ...;
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSDictionary * _Nonnull evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
NSArray *dynamicVales = [evaluatedObject valueForKeyPath: #"dynamicVales.value"];
return [dynamicVales containsObject:searchText];
}];
NSArray *filteredArray = [yourArray filteredArrayUsingPredicate:predicate];
Copied from JhonyKutties answer. Enjoy coding :)

NSPredicate searching for 1:N - 1:N entities

I have an Entity called AcademicYear that has a relationship 1:N with an entity called Subject which has a 1:N relationship with an Entity called SubjectLanguage:
AcademicYear<--->>Subject<--->>SubjectLanguage
I would like to filter the result of a query on AcademicYear in order to be able to gather Subject and SubjectLanguage already filtered by the parameter language.
On runtime i'll use such expression:
academicYear.hasSubjects.hasLanguages.subjectName
and i would like to be sure they are of a specific language filtered by the starting query.
i tried using during the fetch the following predicate (with no result):
NSPredicate *predicateLang = [NSPredicate predicateWithFormat:#"ALL hasSubjects.hasLanguages.language like %#", language];
NEW (Update)
Due to Marcus suggestion i changed the fetch request in this way:
NSError *error;
NSString * language = [[NSLocale preferredLanguages] objectAtIndex:0];
NSString * yearString = [NSString stringWithFormat:#"%d",year];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"belongToAcademicYear.yearId == %# and ANY hasLanguages.language CONTAINS[cd] %#", yearString, language];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"Subject"
inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];
[fetchRequest setPredicate:predicate];
NSArray *fetchedObjects = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error];
return [self arrayToMutableArrayConverter:fetchedObjects];
}
then i tested this solution with the following code:
NSArray * currentSubjects = [self.coordinator fetchSubjectThatBelongsToAcademicYear:1];
Subject * currentSubject = currentSubjects[0];
NSUInteger count = currentSubject.hasLanguages.count;
XCTAssertEqual(count, 1, #"riscontrati troppi languages. non è stato fatto il filtro su language");
I found the filter did not work correctly and i found two languages while i expect one. I'm pretty sure the issue is related with the predicate.
Any support is appreciated
kind regards
Nicolò
You should reverse this a little bit. Instead of fetching AcademicYear and then trying to reach over to SubjectLanguage, search for Subject.
Build your NSFetchRequest against Subject and change the predicate to:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"academicYear == %# and ANY languages.language CONTAINS[cd] %#", academicYear, language];
Some comments:
Your relationships should not start with "has". They should just be academicYear and languages. Singular for to-one and plural for to-many.
Your entity SubjectLanguage should not have a property called language. Something that relates to the entity can be less descriptive. name would work well so that it is the "name of the SubjectLanguage".
Update
It worked exactly right. You found the Subject that has the language and the year you are looking for. It returned that Subject and ALL of its relationships. Since there is more than one language associated with the subject it gave you access to all of the languages.
The predicate is correct, your test is flawed. Your test should be insuring that one of the languages is the one you are looking for.

Setting up a CoreData regular expression predicate for a comma-separated list

I'm beating my head against a wall trying to get a regular expression function to work with a CoreData fetch. I have an attribute named maps on an NSManagedObject subclass that I'm trying to filter by. This maps attribute is a string with a list of id values like so:
1,10,12,8
Here's how my predicate is being constructed:
NSString *format = [NSString stringWithFormat: #"(maps matches '.*\\b%#\\b.*')", _map.mapID];
NSPredicate *predicate = [NSPredicate predicateWithFormat:format];
[request setPredicate:predicate];
NSManagedObjectContext *moc = [[RKManagedObjectStore defaultObjectStore] primaryManagedObjectContext];
_resultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:moc
sectionNameKeyPath:#"name.stringGroupByFirstInitial"
cacheName:nil];
I built the RegEx using this Rubular example: http://rubular.com/r/zELaz19x0T
The mapID value is an integer as a string.
I've read the other questions on SO about the predicate matching against the entire string, but I feel like that should be handled with this pattern using the .* at the beginning and end of the pattern.
The result of this code always returns zero results. If the predicate is not used, all objects are returned as expected. Can anyone see what the issue is here? Thanks!
This is solved with this code:
NSString *format = #"(maps MATCHES %#) OR (maps == '')";
NSString *pattern = [NSString stringWithFormat:#".*(\\b%#\\b).*", _map.mapID];
NSPredicate *predicate = [NSPredicate predicateWithFormat:format, pattern];
I was assuming the format of the predicate was the same as formatting a string, which is not. I was enlightened by this other answer: https://stackoverflow.com/a/12672462/189292

Core Data NSPredicate for toMany Relationsihp

I've got a problem in the formulation of my query; this is the scenario:
based on this db schema, I know the naming part sucks a little, but this is
a project which has been already kicked off, so I've to stick with that.
Now, my goal is to select all the characters with a determined pack_id and a certain category_id, i.e. all the characters from pack 1 in category 5, so this's my NSPredicate
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(category_id == %#) AND (charRelationship.pack_id == %#)", [[cat valueForKey:#"category_id"] stringValue], curPack];
[request setEntity:[NSEntityDescription entityForName:#"Category" inManagedObjectContext:self.managedObjectContext]];
[request setPredicate:predicate];
NSError *error;
NSArray *result = [self.managedObjectContext executeFetchRequest:request error:&error];
as soon as the compiler tries to execute the fetchRequest, it crashes and goes SIGABRT.
I really hate the fact that xcode is not even giving me a clue about the exception so that I could figure it out myself. So after blindly trying to fix it with no success, I wonder if there's anybody out there who could help me.
I've already red a ton of other threads on SO and elsewhere, but I couldn't find any solution.
thanks a lot
-k-
try using ANY for the relationship:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(category_id == %#) AND (ANY charRelationship.pack_id like %#)", [[cat valueForKey:#"category_id"] stringValue], curPack];
Actually you must not store any id separately for an entity that is associated. E.g.: in your case you could just refer to category from the characters entity like:
[NSPredicate predicateWithFormat:#"catRel = %#", cat];
However, I'm not sure what are you going to do with the snippet. Am I right that you have a Category entity stored in your 'cat' variable, and just going to select it from the database with the predicate? I would say, that a category_id already identifies your category... whatever, try not user "charRelationship.pasck_id == %#" but load the corresponding Character entity and use "charRelationship = %#" where you just put your entity.

How to sort NSPredicate

I am trying to sort my array while using NSPredicate.. I have read other places that possibly using NSSortDescriptor could be an option. Having some trouble figuring this out.
I am attempting to sort my array by companyName.
Any advice appreciated, thanks
Greg
- (void)filterSummaries:(NSMutableArray *)all byNameThenBooth:(NSString*) text results:(NSMutableArray *)results
{
[results removeAllObjects];
if ((nil != text) && (0 < [text length])) {
if ((all != nil) && (0 < [all count])) {
NSPredicate *predicate = [NSPredicate predicateWithFormat: #"companyName contains[cd] %# OR boothNumber beginswith %#", text, text];
[results addObjectsFromArray:[all filteredArrayUsingPredicate:predicate]];
}
}
else {
[results addObjectsFromArray:all];
}
}
you have several options how to sort an array:
I'll show a NSSortDescriptor-based approach here.
NSPredicate *predicate = [NSPredicate predicateWithFormat:
#"companyName contains[cd] %# OR boothNumber beginswith %#",
text,
text];
// commented out old starting point :)
//[results addObjectsFromArray:[all filteredArrayUsingPredicate:predicate]];
// create a descriptor
// this assumes that the results are Key-Value-accessible
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:#"companyName"
ascending:YES];
//
NSArray *results = [[all filteredArrayUsingPredicate:predicate]
sortedArrayUsingDescriptors:[NSArray arrayWithObject:descriptor]];
// the results var points to a NSArray object which contents are sorted ascending by companyName key
This should do your job.
The filteredArrayUsingPredicate: function walks through your array and copies all objects that match the predicate into a new array and returns it. It does not provide any sorting whatsoever. It's more of a search.
Use the sorting functions of NSArray, namely sortedArrayUsingComparator:, sortedArrayUsingDescriptors:, sortedArrayUsingFunction:context: and the like, whichever serves you most.
Checkout NSArray Class Reference for details.
BTW: If you want to sort lexically, you may use sortedArrayUsingSelector:#selector(compare:) which will use NSString's compare: function to find the right order.