How to concatenate two column values and compare in ios - ios

In my entity Contact, I have two columns named firstName & lastName. I want to search for a name entered by user in my DB.
For Eg: If user enters only 'William' I can search either its present in first name or last name and show the result.
If the user enters 'William Smith', then I want to get both first name and last name from DB and compares it with search text.
For this i want to use NSPredicate that compares 3 condition:
firstName contains the search text OR
lastName contains the search text OR
firstName+lastName contains the search text.
I used the following predicate for that:
NSString *strSearchType = #"(%# CONTAINS[cd] '%#' OR %# CONTAINS[cd] '%#' OR (%# || %# CONTAINS[cd] '%#')";
I know "||" is the concatenate operator.
But it gives me the following exception:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unable to parse the format string "(firstName CONTAINS[cd] 'rus' OR lastName CONTAINS[cd] 'rus' OR ((firstName || lastName) CONTAINS[cd] 'rus'))"'
Am i doing anything wrong ?

You can try following :
firstName contains the search text OR
lastName contains the search text OR
(search text contains the firstName AND search text contains the lastName).

NSString* stringA=nil; NSString* stringB=nil;
// this would give array of string separated by space
NSArray *searchStrArray = [searchString componentsSeparatedByCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#" "]];
if (searchStrArray.count>0) {
stringA=[searchStrArray objectAtIndex:0];
}
if (searchStrArray.count>1) {
stringB=[searchStrArray objectAtIndex:1];
}
NSPredicate* predicateA= [NSPredicate predicateWithFormat:#“firstName CONTAINS[c] %# OR lastName CONTAINS[c] %#",stringA , stringA];
NSPredicate* resultPredicate=predicateA;
if(stringB){
NSPredicate* predicateB= [NSPredicate predicateWithFormat:#“firstName CONTAINS[c] %# OR lastName CONTAINS[c] %#",stringB , stringB];
resultPredicate=[NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:resultPredicate,predicateB, nil]];
}

Related

NSPredicate execureFetchRequest not working - possibly due to spaces in string

I am trying to search in CoreData for an object that matches both a recordId and a string name, but it doesn't always find the object.
For example, I an searching for an object with id 1000 and name "The Brown Family" (note the 2 spaces between "The" and "Brown").
If I use:
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(recordId == %#") AND (name like[cd] %#)", recordId, name];
with recordId=1000 and name="The Brown Family", the fetch request returns nil.
If I use:
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(recordId == %#"), recordId];
with recordId=1000, it finds the object. If I print the object's name property, I get "The Brown Family".
So the object is there with the correct id and name, but my fetchRequest fails.
What am I doing wrong?
You might need to enclose the value in single quotes...
NSPredicate *pred = [NSPredicate predicateWithFormat:#"(recordId == %#) AND (name like[cd] '%#')", recordId, name];
Sorry but I realise what the problem is now! The name I was searching for had a trailing space after it. So I was looking for say "Toby " whereas "Toby" was stored in Coredata.
Sorry for wasting everyone's time.

NSPredicate unable to parse format string

I can't see any problem with these line of code. Why can't it parse?
[NSPredicate predicateWithFormat:#"(username CONTAINS[cd] %1$#) || "
"(userId CONTAINS[cd] %1$#) || "
"(firstname CONTAINS[cd] %1$#) || "
"(lastname CONTAINS[cd] %1$#)", searchString]"
The Log doesn't help either.
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: 'Unable to parse the format
string "(username CONTAINS[cd] %1$#) || (userId CONTAINS[cd] %1$#) ||
(firstname CONTAINS[cd] %1$#) || (lastname CONTAINS[cd] %1$#)"'
Edit 1:
Okay, it seems like predicateWithFormat doesn't understand "%1$#". I switch it to
[... predicateWithFormat:[NSString stringWithFormat:...]] // (same format as above)
It passed the line. But, the next problem is:
self.filteredUserList = [self.userList filteredArrayUsingPredicate:predicate];
Terminating app due to uncaught exception 'NSUnknownKeyException',
reason: '[ valueForUndefinedKey:]: the entity User is
not key value coding-compliant for the key "a".'
"a" is the keyword I entered in the searchTextBox. WUT?
I printed out the predicate in the debug console, looks nothing wrong:
username CONTAINS[cd] a OR userId CONTAINS[cd] a OR firstname CONTAINS[cd] a OR lastname CONTAINS[cd] a
Edit 2:
Okay, problem solved with this super ugly code:
[NSPredicate predicateWithFormat:#"(username CONTAINS[cd] %#) || "
"(userId CONTAINS[cd] %#) || "
"(firstname CONTAINS[cd] %#) || "
"(lastname CONTAINS[cd] %#)", searchString, searchString, searchString, searchString];
What if I want to expand the search field in the future? I've got to add more parameters? more ", searchString, searchString, searchString"?
SOLVED
Thanks to Ewan and Bannings, giving 2 options to my question. I tested both of them, and they worked liek a charm. Can someone explain the different between those two, and in which case should I use which option?
** NOTE **
Bannings' answer is alright, until my search string contains a single quote ', then the app crash. So I think use Ewan's one is better.
You can do this:
[[NSPredicate predicateWithFormat:#"(username CONTAINS[cd] $str) || ..."] predicateWithSubstitutionVariables:#{#"str": searchString}];
Try change %1$# to '%1$#':
NSString *formatString = [NSString stringWithFormat:#"(username CONTAINS[cd] '%1$#') || (userId CONTAINS[cd] '%1$#') || (firstname CONTAINS[cd] '%1$#') || (lastname CONTAINS[cd] '%1$#')", searchString];
NSPredicate *predicate = [NSPredicate predicateWithFormat:formatString];
You need to add the string for each variable, if you use 5 %# you need to apply either the same searchString value or another value filling for that %#
[NSPredicate predicateWithFormat:#"(username CONTAINS[cd] %1$#) ||
(userId CONTAINS[cd] %1$#) || (firstname CONTAINS[cd] %1$#) ||
(lastname CONTAINS[cd] %1$#)", searchString1,searchString2,searchString3,searchString4]

add componentsseparatedbystring into a predicate with core data

i have a String stored in an Entity (core data) i want to use an NSFetchedResultsController to get data.
string format: abc,ba,x,s,d. this is an array of IDs saved as string.
i want to get only entities that contains at least an IDs in that string.
the problem is if i use CONTAIN in the predicate and search for "a" i will get a wrong result.
could you please tel me if it's possible to add something like "componentsseparatedbystring" in a predicate so i can iterate and use "in"in the result or if there's an other solution, thanks.
You can use the "MATCHES" operator in a predicate, which does a
regular expression match:
NSString *searchID = #"a";
NSString *pattern = [NSString stringWithFormat:#"(^|.*,)%#(,.*|$)",
[NSRegularExpression escapedPatternForString:searchID]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ID MATCHES %#", pattern];
The pattern (^|.*,)TERM(,.*|$) searches for TERM which is preceded
by either the start of the string or a comma, and followed by the
end of the string or another comma.
First convert your array of ID's into an NSArray:
NSArray *arrayOfIds = [stringOfIds componentsSeparatedByString:#","];
Then use an IN predicate on your fetch:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ID IN %#", arrayOfIds];
this assumes your database column is called "ID", and your comma-separated string of ID's is stringOfIds.
finally i will use a dirty solution: we have 4 possibilities:
string format= a
string format= a,..
string format= ..,a
string format= ..,a,..
so the predicate could be:
[NSPredicate predicateWithFormat:#"(ID LIKE %# OR
ID CONTAINS %# OR
ID CONTAINS %# OR
ID ENDSWITH %#)",
searchedID,
[NSString stringWithFormat:#"%#,", searchedID],
[NSString stringWithFormat:#",%#", searchedID],
[NSString stringWithFormat:#",%#,", searchedID]]
but this is a dirty solution, i really want something cleaner.

NSPredicate and BEGINSWITH with CloudKit : Field value type mismatch

I have a table "Store" with the attributes "name" of type String (indexes checked for sort query and search).
I want to execute a SQL like query to find all stores with name beginning with a substring.
So I tried this predicate :
NSPredicate *pred = [NSPredicate predicateWithFormat:#"ANY name BEGINSWITH %#",substring];
CKQuery *query = [[CKQuery alloc]initWithRecordType:#"Store" predicate:pred];
CKQueryOperation *queryOperation = [[CKQueryOperation alloc] initWithQuery:query];
queryOperation.desiredKeys = #[#"name",#"category"];
NSMutableArray *results = [[NSMutableArray alloc] init];
queryOperation.recordFetchedBlock = ^(CKRecord *record) {
[results addObject:record];
};
queryOperation.queryCompletionBlock = ^(CKQueryCursor *cursor, NSError *error) {
//my own code
}
[publicDatabase addOperation:queryOperation];
I have this error :
<CKError 0x1887a540: "Invalid Arguments" (12/1009); "Field value type mismatch in query predicate for field 'name'"> for query : <CKQuery: 0x18882650; recordType=Store, predicate=ANY name BEGINSWITH "mysubstring">
My guess is that you need to get rid of the "ANY".
The ANY and SOME aggregate operators may be combined with the IN and
CONTAINS operators to perform list membership tests.
So ANY is for lists (Arrays). Since 'name' is a String, it is not a list, hence the mismatch error: "Field value type mismatch in query predicate for field 'name'"
Wild guess...try:
[NSPredicate predicateWithFormat:#"ANY {'name','category'} BEGINSWITH %#",substring];
That may be an incorrect reversal of key/value but it might work.
A completely different approach would be to create a third field called "nameAndCategory", append the two strings and add them to the field. Then figure out how to do the full text search (tokenized string search) with the predicate:
[NSPredicate predicateWithFormat:#"self contains '%#'",substring];
or
[NSPredicate predicateWithFormat:[NSString stringWithFormat:#"self contains '%#'",substring];
But perhaps the only sure approach is to do two searches and combine the results.

Escape nonbreaking space as argument in NSPredicate

I want to filter out empty records, but I am not able to remove the records which has no data but a series of space..
I have a Entity A with attribute "name" . I want to query all those objects of Entity A which has some text value for attribute name and not "series of space",In other words I need to incorporate this in the query -->
[name stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet].length!=0
But all I could was check the length of string INCLUDING the whitespace which is not right.
I am using-->
NSPredicate* predicate = [NSPredicate predicateWithFormat:#"name!=' ' or name.length!=0 "];
Your suggetions are welcome.
you can trim the string and check for the lenght of the string like this:
NSString *trimmedString = [aRecordString stringByTrimmingCharactersInSet:[[NSCharacterSet characterSetWithCharactersInString:#" "]]];
if (trimmedString.lenght > 0) {
// the string if valid or at least is not just a spaces string
}
else {
// the string is not valid or was just a spaces string
}
so if you want to apply this check in a predicate you can use a predicate with block:
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
// do your checks here and return TRUE or FALSE if the ovject match your criteria
}];
If you're trying to remove objects where a specific string attribute contains only spaces, use a regex-based predicate, and then delete whatever objects you find.
A fetch with a predicate like this would find all of those objects:
NSPredicate *reqPredicate = [NSPredicate predicateWithFormat:#"attributeName matches ' +'"];
Once found, use -[NSManagedObjectContext deleteObject:] to delete the resulting objects.

Resources