Core Data Query Multiple Columns with Single Search String - iphone

I have a simple Core Data model with two string attributes (size and category). Given a search string like 'small widget' is it possible to return records that match all the query words to at least one attribute (i.e. all records with 'small' size and 'widget' category)? I currently have:
NSString *search = #"small widget"
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"size contains[cd] %# OR category contains[cd] %#", search, search];
...
This won't return any results (as no category or size equals "small widget"). Any suggestions? Note that the search strings will be user entered from a text field and may come in any order so I can't split up manually.

I haven't tested it but it looks like your want this:
NSString *search = #"small widget";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%# contains[cd] size AND %# contains[cd] category", search, search];
The search string would contain (or not) the size and the category, you should ask if the current size or category is contained on the search string.
You could also split the search string and modify your predicate. You should them to identify the one that performs better
NSString *search = #"small widget";
NSArray *array = [search componentsSeparatedByString:#" "];
NSMutableArray *subPredicates = [NSMutableArray array];
for (NSString *q in array) {
[subPredicates addObject:
[NSPredicate predicateWithFormat:#"size contains[cd] %# OR category contains[cd] %#", q, q]];
}
NSCompoundPredicate *predicate = [[[NSCompoundPredicate alloc] initWithType:NSAndPredicateType
subpredicates:subPredicates] autorelease];

For Swift you can fetch multiple columns by using propertiesToFetch
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "<YOUR ENTITY NAME>")
let predicate = NSPredicate(format: "<YOUR PREDICATE>")
fetchRequest.predicate = predicate
fetchRequest.resultType = .dictionaryResultType
// Here is where you can query multiple columns, you can add as much columns as you want and fetch them as an array of dictionaries
fetchRequest.propertiesToFetch = ["column1", "column2", "column3, "column4"]
let sort = NSSortDescriptor(key: "column1", ascending: false)
fetchRequest.sortDescriptors = [sort]

Related

Searching through an array and putting it into UITableViewCell in swift

Right now I have an array that has around 100000 elements. I've created a search bar for it but I think due to the number of elements that it is parsing through my phone lags when using the search bar. Is there a way to fix this?
Have you tried using NSPredicate?
NSMutableArray *array = [NSMutableArray arrayWithObjects:#"Nick", #"Ben", #"Adam", #"Melissa", nil];
NSPredicate *bPredicate = [NSPredicate predicateWithFormat:#"SELF beginswith[c] 'a'"];
NSArray *beginWithA = [array filteredArrayUsingPredicate:bPredicate];
// beginWithA contains { #"Adam" }.
NSPredicate *sPredicate = [NSPredicate predicateWithFormat:#"SELF contains[c] 'e'"];
[array filterUsingPredicate:sPredicate];
// array now contains { #"Nick", #"Ben", #"Melissa" }
That being said, with 100000 elements, wouldn't a persistent store make more sense?

How to join two strings for NSPredicate, ie firstname and lastname

I have a Person Object which has two NSString properties; firstName and lastName.
I'm currently using an NSPredicate like so:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(firstName contains[cd] %#) OR (lastName contains[cd] %#)", searchText, searchText];
So, for example, say I'm searching for the name "John Smith". In my search bar if I type "Joh", then John Smith will appear as an option. This is good and fine, but if I type in "John Sm" it will go blank.
How can I join firstName and lastName in the predicate so if I was searching for "John Sm" then John Smith would still appear as an option.
I hope this makes sense. Thanks.
EDIT:
To add a bit more clarification, I'm using the SearchDisplayController delegate method:
-(void)filterContentForSearchText:(NSString *)searchText scope:(NSString *)scope;
and I'm using the predicate like so:
newArray = [personObjectArray filteredArrayUsingPredicate:predicate];
Try this,
NSString *text = #"John Smi";
NSString *searchText = [text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
NSArray *array = [searchText componentsSeparatedByString:#" "];
NSString *firstName = searchText;
NSString *lastName = searchText;
NSPredicate *predicate = nil;
if ([array count] > 1) {
firstName = array[0];
lastName = array[1];
predicate = [NSPredicate predicateWithFormat:#"(firstName CONTAINS[cd] %# AND lastName CONTAINS[cd] %#) OR (firstName CONTAINS[cd] %# AND lastName CONTAINS[cd] %#)", firstName, lastName, lastName, firstName];
} else {
predicate = [NSPredicate predicateWithFormat:#"firstName CONTAINS[cd] %# OR lastName CONTAINS[cd] %#", firstName, lastName];
}
NSArray *filteredArray = [people filteredArrayUsingPredicate:predicate];
NSLog(#"%#", filteredArray);
Output:
(
{
firstName = John;
lastName = Smith;
}
)
Here text represents the searched text. The advantage with the above is, even if you pass text = #"Smi Joh"; or text = #"John "; or text = #" smi"; or text = #"joh smi ";, it will still show the above output.
The solution suggested above will not work with search strings that have more than two words. Here is a more thorough implementation in swift. This solution also allows for adding more fields on a record, if your goal is to implement a full text search across name, email, phone number, etc. In that case just update the NSPredicate to OR newField CONTAINS[cd] %# and be sure to add the extra $0 in the list of string replacements.
let searchText = search.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
let words = searchText.componentsSeparatedByCharactersInSet(NSCharacterSet.whitespaceCharacterSet())
let predicates = words.map { NSPredicate(format: "firstName CONTAINS[cd] %# OR lastName CONTAINS[cd] %#", $0,$0) }
let request = NSFetchRequest()
request.predicate = NSCompoundPredicate(type: NSCompoundPredicateType.AndPredicateType, subpredicates: predicates)
you can concatenate the fields into two common fields (firstLastName and lastFirstName )
- (NSString *)firstLastName {
return [NSString stringWithFormat:#"%# %#", self.firstName, self.lastName];
}
- (NSString *)lastFirstName {
return [NSString stringWithFormat:#"%# %#", self.lastName, self.firstName];
}
and then filter on this fields using 'contains[cd]'
[NSPredicate predicateWithFormat:#"(firstLastName contains[cd] %#) OR (lastFirstName contains[cd] %#)" , self.searchBar.text, self.searchBar.text];

How to implement searching of a string in an array of dictionaries?

I have an application in which i have an nsmutable array created like this .`
NSDictionary *memberInfo = [self.currentChannel infoForMemberWithID:memberID];
for(int i=0;i<self.currentChannel.memberCount;i++)
{
[searchfriendarray addObject:memberInfo];
}
NSLog(#"dfdfdfsear%#",searchfriendarray);
The response i am getting is this
dfdfdfsear(
{
name = baddd;
status = "<null>";
},
{
name = baddd;
status = "<null>";
}
)
`
Now i want to search an nsstring in this.with the method
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
Can anybody help me in implementing the search and load my tableview according to that?
Use NSPredicate to filter the array.
NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:#"name contains[cd] %#", searchString];
[yourMutableArray filterUsingPredicate:filterPredicate];
You can use NSPredicate for this.
Predicate
A predicate is a useful object that filters an array. It’s a bit like having a select with a simple where clause in SQL.
If you have array in this format
NSMutableArray *yourAry;
(
{
category = classic;
quote = "Frankly, my dear, I don't give a damn.";
source = "Gone With the Wind";
},
{
category = classic;
quote = "Here's looking at you, kid.";
source = Casablanca;
}
{
category = modern;
quote = "I solemnly swear that I am up to no good.";
source = "Harry Potter and the Prisoner of Azkaban";
},
{
category = modern;
quote = "You like pain? Try wearing a corset.";
source = "Pirates of the Caribbean";
}
)
And you want to search all the array which category = classic then we can use NSPredicate.
NSString *selectedCategory = #"classic";
//filter array by category using predicate
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"category == %#", selectedCategory];
NSArray *filteredArray = [self.movieQuotes filteredArrayUsingPredicate:predicate];
Follow this blog if you need any help MyBlog

NSPredicate against NSArray in iphone

How can I use NSPredicate to filter the values from array ivar against search query ? I manage to use NSPredicate against string ivar. I have one user defined class named as "User" which is used to create User from AddressBook. Here is my code
// User Defined Class named as "User"
// In User.h
#interface User : NSObject {
NSString *firstName;
NSString *lastName;
NSString *company;
UIImage *userImage;
NSArray *phoneNumberArray;
NSArray *emailArray;
NSArray *urlArray;
NSArray *addressArray;
NSString *notes;
NSString *dateOfBirth;
}
// In SearchViewController.m
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText{
// contactsArray is NSMutableArray
contactsArray = [Search searchContactsWithQuery:searchText];
NSLog(#"contactsArray count => %d",[contactsArray count]);
[contactsTableView reloadData];
}
// In Search.m
+(NSMutableArray*)searchContactsWithQuery:(NSString*)query {
NSLog(#"Query => %#",query);
NSMutableArray* predicates=[[NSMutableArray alloc] init];
// Create all predicates
NSPredicate * firstNamePredicate = [NSPredicate predicateWithFormat:#"firstName contains %#",query];
[predicates addObject:firstNamePredicate];
NSPredicate * lastNamePredicate = [NSPredicate predicateWithFormat:#"lastName contains %#",query];
[predicates addObject:lastNamePredicate];
NSPredicate * companyPredicate = [NSPredicate predicateWithFormat:#"company contains %#",query];
[predicates addObject:companyPredicate];
// Don't know how to use on array
// === START of ARRAY Predicate ====
NSPredicate *phoneNoPredicate = [NSPredicate predicateWithFormat:#"phoneNumberArray IN %#",query];
[predicates addObject:phoneNoPredicate];
NSPredicate *emailPredicate = [NSPredicate predicateWithFormat:#"emailArray contains %#",query];
[predicates addObject:emailPredicate];
NSPredicate *urlPredicate = [NSPredicate predicateWithFormat:#"urlArray contains %#",query];
[predicates addObject:urlPredicate];
NSPredicate *addressPredicate = [NSPredicate predicateWithFormat:#"addressArray contains %#",query];
// === END of ARRAY Predicate ====
NSPredicate *notesPredicate = [NSPredicate predicateWithFormat:#"notes contains %#",query];
[predicates addObject:notesPredicate];
NSPredicate *dobPredicate = [NSPredicate predicateWithFormat:#"dateOfBirth contains %#",query];
[predicates addObject:dobPredicate];
// Add predicates to array
NSPredicate *compoundPredicate = [NSCompoundPredicate orPredicateWithSubpredicates:predicates];
NSArray * filteredArray = [APP_DELEGATE.allUsersArray filteredArrayUsingPredicate:compoundPredicate];
return [NSMutableArray arrayWithArray:filteredArray];
}
phoneNumberArray,emailArray,urlArray & addressArray are used because user may have multiple entries for phone no , email, address , url like HOME , WORK, iPhone, Other etc.
How can I use predicates on array? Any kind of help is appreciated. Thanks
You are doing the correct way, but you need to create your Predicates with String values instead of passing the Objects directly.
You need to use %k instead of %# like
[NSPredicate predicateWithFormat:#"firstName contains %k",query];
Please let me know if this helps.

Creating NSPredicate from string

I need to form a predicate from multiple arrays of values. So I thought I can initially form a string with all the values, and then pass that string for usage in a predicate, i.e
NSString* stringForPredicate = [[NSString alloc] init];
if (fromDate != nil) {
stringForPredicate = [stringForPredicate stringByAppendingFormat:#"(Date > %# AND Date < %#)", fromDate, toDate];
}
There are further calculations that I do to form the final predicate string.
Now, I want the predicate to use this string. I thought that something like this would work:
NSPredicate* filterPredicate = [NSPredicate predicateWithFormat:#"%#",stringForPredicate];
But it doesnt and throws the exception:
'Unable to parse the format string "%#"'
Is there a way I can make this work?
thanks,
The stringForPredicate variable actually contains the format_string. So, you need to assign that variable in place of the format_string, and pass the args after that, seperated by commas, like this.
NSSring *stringForPredicate = #"(Date > %# AND Date < %#)";
NSPredicate* filterPredicate = [NSPredicate predicateWithFormat:stringForPredicate, fromDate, toDate];
For compound predicates:
NSMutableArray *subPredicates = [NSMutableArray array];
if (fromDate != nil) {
NSPredicate *from_predicate = [NSPredicate predicateWithFormat:#"Date > %#", fromDate];
[subPredicates addObject:from_predicate];
}
if (toDate != nil) {
NSPredicate *to_predicate = [NSPredicate predicateWithFormat:#"Date < %#", toDate];
[subPredicates addObject:to_predicate];
}
NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:subPredicates];
Wont this do it?
NSPredicate* filterPredicate = [NSPredicate predicateWithFormat:stringForPredicate];
or you have to do this
NSPredicate* filterPredicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:#"%#",stringForPredicate]];
From the reference documents
Format String Summary
It is important to distinguish between
the different types of value in a
format string. Note also that single
or double quoting variables (or
substitution variable strings) will
cause %#, %K, or $variable to be
interpreted as a literal in the format
string and so will prevent any
substitution.
Try this
stringForPredicate = [stringForPredicate stringByAppendingFormat:#"(Date > %#) AND (Date < %#)", fromDate, toDate];
For those using swift:
let predicate = NSPredicate.init("Date > %# AND Date < %#", fromDate, toDate);