I have an array of data that is displayed in a table. The array has multiple fields, including two specific ones I want to filter, the "call type" and the "county". The value for "call type" is either an "f" or "e" and the value for the county is either "w" or "c". I want to have 4 UISwitch's to to either turn on/off the "w", turn on/off the "c" etc. Its hard to explain but if you go to this website and look at the top right corner, its exactly what I want to do. http://www.wccca.com/PITS/ Out of the 4 filters, two filters control the county field, and two filters control the call type field. but they all operate independently. How would I go about accomplishing this? Would I use NSPredicate to create a new array each time something is filtered or what? Thanks.
You could definitely use an NSPredicate for this. Probably the easiest thing to do would be to use the same IBAction for all four switches and have it do a recalculation:
- (IBAction)anySwitchDidChange:(id)sender
{
// make a set of all acceptable call types
NSMutableSet *acceptableCallTypes = [NSMutableSet set];
if(self.fSwitch.on) [acceptableCallTypes addObject:#"f"];
// ... etc, to create acceptableCallTypes and acceptableCounties
NSPredicate *predicate =
[NSPredicate predicateWithFormat:
#"(%# contains callType) and (%# contains county)",
acceptableCallTypes, acceptableCounties];
/*
this predicate assumes your objects have the properties 'callType' and
'county', and that you've filled the relevant sets with objects that would
match those properties via isEqual:, whether strings or numbers or
anything else.
NSDictionaries are acceptable since the internal mechanism used here is
key-value coding.
*/
NSArray *filteredArray = [_sourceArray filteredArrayUsingPredicate:predicate];
// send filteredArray to wherever it needs to go
}
Using predicateWithFormat: causes the text to be parsed right there and then. In this case that should be no problem whatsoever but in general you can create the predicates in advance and supply only the parameters at the relevant moment, should you ever end up using one in a really time critical area.
Related
I've been working on a search algorithm all afternoon and I'd like some opinions. Some of what I'm doing is specific to iOS, but the general concepts are not.
I'm trying to display a set of data, a directory. In the directory I have departments and people. I know this sounds like a textbook example, hear me out. It's not homework, I promise. (I can provide screenshots of what I'm working on.)
I have an array of entries, where there are those two kinds of directory entries. I need to sort the entries by name, then break up the array into smaller arrays, where each sub-array contains the entries that begin with the same letter.
Additionally, I need to account for a search string that the user may enter.
My general process is this:
Filter all the entries that match the type and search string if there is one. For this step I use an NSPredicate:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"type == %i AND searchableContents B[cd] %#", type, searchString];
if (!searchString || searchString.length == 0)
{
predicate = [NSPredicate predicateWithFormat:#"type == %i", type];
}
NSArray *array = [_directoryContents filteredArrayUsingPredicate:predicate];
Sort the results alphabetically.
array = [array sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [((BRKDirectoryEntry *)obj1).comperableTitle compare:((BRKDirectoryEntry *)obj2).comperableTitle];
}];
Break up the results into smaller arrays. For performance, I skip this step if we're searching, but it doesn't seem to help.
if(alphabetized)
{
array = [self _alphabetizedArrayFromPresortedArray:array];
}
The performance of this on a total of 950 entries is abysmal.
Now, for my default display, I can get away with simply caching the sorted data in memory, and then display and scrolling performs nicely, but for search-as-I-type, there's simply no way to achieve the smooth performance that users expect.
Any pointers or tips?
Yes. Forget files and keep it in a database. Create your indexes Everything becomes a simple SQL statement.
I have a NSMutableArray of NSDictionaries. I would like to be able to filter dynamically.
For example, I have 3 types of filters
- area
- item
- type
if area is chossen then I would like to filter the Array with the area predicate, However if the user then chooses to filter item too, the currently filtered area array will then have the item filter applied too it.
However if the area filter is removed then I would like to show the new item filter.
It gets even more complicated when type is introduced, however I am struggling to get this to work correctly and don't really know where to start with the logic of it.
I can get the array to filter based off the last selected predicate. So if I choose area then items the current filter will only be items not both.
Try something like:
NSMutableArray *predicates = [NSMutableArray array];
if (...) {
[predicates addObject:...];
}
NSCompoundPredicate *p = [[NSCompoundPredicate alloc] initWithType:NSAndPredicateType subpredicates:predicates];
Where you add the individual predicates to the array if they are required. This is assuming that at least one predicate does need to be applied.
I'm working on an app where I have two entities, Post <-->> StreamType. When I create posts I assign StreamType:s like this:
// streamType == one of my default streamTypes
[post addStreamTypesObject:streamType];
My predicate for finding posts that have a certain StreamType looks like this:
predicate = [NSPredicate predicateWithFormat:#"ANY streamTypes.type = %#", [NSNumber numberWithInt:self.pageType]];
I'm not sure why this happens. Any ideas?
Edit
What I basically want is to fetch all Posts that have the right StreamType. Seemed after all that my fetchrequest only returns 1 item from the database. So probably nothing wrong with my tableview.
Edit 3
The problem was with my relationship, should be many-to-many, not one to many. Therefore it only returned one Post item.
First: test if the other code is ok. Simply, remove the predicate (comment the setPredicate line). You should see ALL objects in your tableview.
Right?
Second: check if self.pageType is set correctly. I don't see in your code how you set self.pageType
Test your predicate, add an NSLog like this and check if the result is ok:
NSLog(#"ANY streamTypes.type = %d", self.pageType);
Third:
As far as I understand, you have this situation:
One Post has only one stream type
One stream type has multiple posts.
The ANY keyword is used in situations where you want, for example, obtain all stream types where a particular condition is satisfied at least one time. for example (assuming you have a "content" instance variable on your post, containing the text of the post)
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"ANY post.content CONTAINS[cd] %#", #"aWord"];
In this case, you will obtain all stream types in which there are posts containing "aWord" particular word in the text.
Your case is simpler. I think that you should simply use:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"streamTypes.type = %d", self.pageType];
Try and let me know
I was just having the same issue using Core Data with Swift and just wanted to add this answer in incase anybody else is having a similar issue.
This was my NSPredicate code:
let predicate = NSPredicate(format: "routine == %#", self.selectedRoutine)
Routine holds multiple exercise objects which I was trying to return. An exercise can only have one routine but a routine can have many exercises.
Turns out I'd forgotten to select 'To Many' as the relationship type for the exercises relationship in my routine entity using the Data Model inspector. It was set as 'To One'.
I have a NSMutableArray containing custom objects of type Episode. Each of these objects has multiple NSStrings as properties. Now I would like to filter the array to check if I have this episode (parsed from an XML) already and update it or create a new Episode object.
I use the following code:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"weblink = %#", currentEpisode.weblink];
NSArray* hits = [appDelegate.episodesList filteredArrayUsingPredicate:predicate];
currentEpisode is the episode I parsed from the XML and I want to check for, episodeList is my NSMutableArray with Episode objects. weblink is one of the NSString properties containing an URL.
When I check on weblink everything works fine. BUT URLs in Podcastfeeds can change so I want to check on an other property called kuhid which is a unique identifier provided in the feed. 'kuhid' is also an NSString (example: 644ED540-EDCA-4D4F-882E-4B3106DDAAB3). When I check on 'kuhid' the predicate never matches and I get duplicates.
Both properties are NSStrings, both correctly synthesized. Same if I try one of my other NSString (e.g. title) propierties.
Have anybody an idea why that work only with weblink and not with any of my other properties?
Are you sure your string exactly matches, namely you don't have leading or trailing spaces, or lowercase vs. uppercase, or different dashes used (long dash vs. short dash for example) or invisible characters?
Try to log the NSData representation of both strings to compare them byte by byte in the debugger just to be sure
Im building an app where users will be able to filter through a set of photos based on 4 parameters:
model http://seismicdevelopment.com/shot.png
FeedType and PhotoType can be one of three possible values respectively. Market and Tag can contain multiple values that describe the photo. I have the ui setup where for FeedType/PhotoType is a series of UISwitches for each param. If you turn one value on, the others in that group will turn off.
Market/Tag have UIButtons that can be made selected/normal. Any selected button should be added to the filtering to narrow results.
I can't seem to figure out how I should write the filtering logic to be most effective. Essentially on any control change, I need to rewrite the predicate filter but I can't seem to wrap my head around how I can link the separate param filters together.
For Tag/Market I'd also like to add a disabled state for UIButtons that have no records, or no records based on the currently selected buttons.
It is hard to tell a precise advice without knowing what kind the parameters are, but, as far as i can see, you need to have four variables,more likely,NSStrings, and every time the user changes the parameter, you create a new fetch predicate, by appending all that variables with the right format. That's how i see it in general.
UPDATE
So, with Market and Tag Butons everything is quite obvious - you shoud use NSCompondPredicate. You'll need an array of predicates, so make a property.
#property (strong) NSMutableArray *subpredicates;
Then, each time user touches the button, you add or remove that narrowing predicate.
- (void)tagButtonTouched:(UIButton *)button
{
if (thisTagIsNotYetSelected)
[subpredicates addObject:[NSPredicate predicateWithFormat:#"ANY tags.name == %#", button.titleLabel.text]];//or some other predicate based on that button title
else
[subpredicates removeObject:[NSPredicate predicateWithFormat:#"ANY tags.name == %#",button.titleLabel.text] ]];
}
Same thing for Markets
- (void)marketButtonTouched:(UIButton *)button
{
if (thisMarketIsNotYetSelected)
[subpredicates addObject:[NSPredicate predicateWithFormat:#"ANY markets.name == %#", button.titleLabel.text]];//or some other predicate based on that button title
else
[subpredicates removeObject:[NSPredicate predicateWithFormat:#"ANY markets.name == %#",button.titleLabel.text] ]];
}
You should also add two variables,i guess,NSPredicates or NSString,that will hold the value of selected FeedType and PhotoType,lets's call them feedTypePredicate and photoTypePredicate respectivly. When user touches those switches(why not segmentedControls?), you just change their's values.
And, finally, you should just combine all those predicates into one and make your fetch!
if(photoTypePredicate)
[subpredicates addObject:photoTypePredicate];
if(feedTypePredicate)
[subpredicates addObject:feedTypePredicate];
NSPredicate *finished = [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates];//Your final predicate