NSPredicate 'OR' filtering based on an NSArray of keys - objective-c

Consider the following NSArray:
NSArray *dataSet = [[NSArray alloc] initWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:#"abc", #"key1", #"def", #"key2", #"hij", #"key3", nil],
[NSDictionary dictionaryWithObjectsAndKeys:#"klm", #"key1", #"nop", #"key2", nil],
[NSDictionary dictionaryWithObjectsAndKeys:#"qrs", #"key2", #"tuv", #"key4", nil],
[NSDictionary dictionaryWithObjectsAndKeys:#"wxy", #"key3", nil],
nil];
I am able to filter this array to find dictionary objects that contain the key key1
// Filter our dataSet to only contain dictionary objects with a key of 'key1'
NSString *key = #"key1";
NSPredicate *key1Predicate = [NSPredicate predicateWithFormat:#"%# IN self.#allKeys", key];
NSArray *filteretSet1 = [dataSet filteredArrayUsingPredicate:key1Predicate];
NSLog(#"filteretSet1: %#",filteretSet1);
Which appropriately returns:
filteretSet1: (
{
key1 = abc;
key2 = def;
key3 = hij;
},
{
key1 = klm;
key2 = nop;
}
)
Now, I am wanting to filter the dataSet for dictionary objects containing ANY of the keys in an NSArray.
For example, using the array: NSArray *keySet = [NSArray arrayWithObjects:#"key1", #"key3", nil]; I want to create a predicate that returns and array of any dictionary objects that contain either 'key1' or 'key3' (ie. in this example all dictionary objects would be returned except for the third object - as it does not contain either 'key1' or 'key3').
Any ideas on how I would achieve this? Would I have to use a compound predicate?

The ANY operator of NSPredicate covers this:
NSSet *keys = [NSSet setWithObjects:#"key1", #"key3", nil];
NSPredicate *key1Predicate = [NSPredicate predicateWithFormat:#"any self.#allKeys in %#", keys];

Do this:
NSString *key = #"key1";
NSString *key1 = #"key3";
NSPredicate *key1Predicate = [NSPredicate predicateWithFormat:#"%# IN self.#allKeys OR %# IN self.#allKeys",key,key1];
NSArray *filteretSet1 = [dataSet filteredArrayUsingPredicate:key1Predicate];
NSLog(#"filteretSet1: %#",filteretSet1);
Works perfectly for me. Hope Helpful

Altough the question has been answered, you could also use block for more granularity:
NSArray *filter = [NSArray arrayWithObjects:#"key1", #"key3",nil];
NSPredicate *filterBlock = [NSPredicate predicateWithBlock: ^BOOL(id obj, NSDictionary *bind){
NSDictionary *data = (NSDictionary*)obj;
// use 'filter' and implement your logic and return YES or NO
}];
[dataSet filteredArrayUsingPredicate:filterBlock];
That could be rearranged as you want, maybe within its own method.

Related

Using NSPredicate to filter an NSMutableArray of NSDictionaries to find if a "key" exists

I have an NSMutableArray of dictionaries. I am using NSPredicate to filter through the array to find if a dictionary with a particular key exists or not.
I have referred to various examples, one of the closest is here: Using NSPredicate to filter an NSArray based on NSDictionary keys. However, I don't wish to have a value to the key. My problem is that I want to find the key first. I tried different syntaxes, but it did not help.
What I have done so far:
NSString *key = #"open_house_updated_endhour";
NSPredicate *predicateString = [NSPredicate predicateWithFormat:#"%K", key];
//NSPredicate *predicateString = [NSPredicate predicateWithFormat:#"%K contains [cd]", key]; Doesn't work.
//NSPredicate *predicateString = [NSPredicate predicateWithFormat:#"%K == %#", key]; Won't work because it expects a value here.
NSLog(#"predicate %#",predicateString);
NSArray *filtered = [updatedDateAndTime filteredArrayUsingPredicate:predicate]; // updatedDateAndTime is the NSMutableArray
A dictionary delivers nil as value for absent key. So simply compare the key against nil.
NSPredicate *predicateString = [NSPredicate predicateWithFormat:#"%K==NULL", key]; // Dictionaries not having the key
NSPredicate *predicateString = [NSPredicate predicateWithFormat:#"%K!=NULL", key]; // Dictionaries having the key
Try it:
NSArray *Myarray = [NSArray arrayWithObject:[NSMutableDictionary dictionaryWithObject:#"my hello string" forKey:#"name"]];
NSArray *filteredarray = [Myarray filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"(name == %#)", #"my hello string"]];
NSLog("%#",filteredarray);
Another example :
NSString *mycategory = #"iamsomeone";
NSArray *myitems = #[#{ #"types" : #[#"novel", #"iamsomeone", #"dog"] },
#{ #"types" : #[#"cow", #"iamsomeone-iam", #"dog"] },
#{ #"types" : #[#"cow", #"bow", #"cat"] }];
NSPredicate *mypredicate = [NSPredicate predicateWithBlock:^BOOL(id evaluatedObject, NSDictionary *bindings) {
NSArray *categories = [evaluatedObject objectForKey:#"types"];
return [categories containsObject:mycategory];
}];
NSArray *outpuArray = [myitems filteredArrayUsingPredicate:mypredicate];
NSLog(#"hello output:%#",outpuArray);

how to filter only a specific key value from an array of dictionaries using NSPredicate

NSArray *details = [NSArray arrayWithObjects:#"name",#"age",#"gender",nil];
NSArray *ray1 = #[#"ray",#"23",#"male"];
NSArray *steve1 = #[#"steve",#"23",#"male"];
NSDictionary *ray = [NSDictionary dictionaryWithObjects:ray1 forKeys:details];
NSDictionary *steve = [NSDictionary dictionaryWithObjects:steve1 forKeys:details];
NSArray *register = #[ray,steve];
i need to filter only the values of key using NSPredicate
this prints the values of the key "name" of all the dictionaries
NSArray *details = [NSArray arrayWithObjects:#"name",#"age",#"gender",nil];
NSArray *ray1 = #[#"ray",#"23",#"male"];
NSArray *steve1 = #[#"steve",#"23",#"male"];
NSDictionary *ray = [NSDictionary dictionaryWithObjects:ray1 forKeys:details];
NSDictionary *steve = [NSDictionary dictionaryWithObjects:steve1 forKeys:details];
NSArray *reg = #[ray,steve];
NSPredicate *pr = [NSPredicate predicateWithValue:YES];
NSArray *ar = [[reg valueForKey:#"name"] filteredArrayUsingPredicate:pr];
NSLog(#"%#",ar);

Sort NSMutableArray based on strings from another NSArray

I have an NSArray of strings that I want to use as my sort order:
NSArray *permissionTypes = [NSArray arrayWithObjects:#"Read", #"Write", #"Admin", nil];
I then have a NSMutableArray that may or may not have all three of those permissions types, but sometimes it will only be 2, sometimes 1, but I still want it sorted based on my permissionsTypes array.
NSMutableArray *order = [NSMutableArray arrayWithArray:[permissions allKeys]];
How can I always sort my order array correctly based on my using the permissionTypes array as a key?
I would go about this by creating a struct or an object to hold the permission types.
Then you can have...
PermissionType
--------------
Name: Read
Order: 1
PermissionType
--------------
Name: Write
Order: 2
and so on.
Then you only need the actual array of these objects and you can sort by the order value.
[array sortUsingComparator:^NSComparisonResult(PermissionType *obj1, PermissionType *obj2) {
return [obj1.order compare:obj2.order];
}];
This will order the array by the order field.
NSMutableArray *sortDescriptors = [NSMutableArray array];
for (NSString *type in permissionTypes) {
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:type ascending:YES] autorelease];
[sortDescriptors addObject:descriptor];
}
sortedArray = [myArray sortedArrayUsingDescriptors:sortDescriptors];
Use whichever sorting method on NSMutableArray you prefer, you will either provide a block or a selector to use for comparing two elements. In that block/selector rather than comparing the two strings passed in directly look each up in your permissionTypes array using indexOfObject: and compare the resulting index values returned.
I suggest you another approuch:
- (void)viewDidLoad
{
[super viewDidLoad];
arrayPermissions = [[NSMutableArray alloc] init];
NSDictionary *dicRead = [NSDictionary dictionaryWithObjectsAndKeys:
#"Read", #"Permission", nil];
NSDictionary *dicWrite = [NSDictionary dictionaryWithObjectsAndKeys:
#"Write", #"Permission", nil];
NSDictionary *dicAdmin = [NSDictionary dictionaryWithObjectsAndKeys:
#"Admin", #"Permission", nil];
NSLog(#"my dicRead = %#", dicRead);
NSLog(#"my dicWrite = %#", dicWrite);
NSLog(#"my dicAdmin = %#", dicAdmin);
[arrayPermissions addObject:dicRead];
[arrayPermissions addObject:dicWrite];
[arrayPermissions addObject:dicAdmin];
NSLog(#"arrayPermissions is: %#", arrayPermissions);
// create a temporary Dict again
NSDictionary *temp =[[NSDictionary alloc]
initWithObjectsAndKeys: arrayPermissions, #"Permission", nil];
// declare one dictionary in header class for global use and called "filteredDict"
self.filteredDict = temp;
self.sortedKeys =[[self.filteredDict allKeys]
sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"sortedKeys is: %i", sortedKeys.count);
NSLog(#"sortedKeys is: %#", sortedKeys);
}
hope help

Sorting NSMutableArray by date

I want to sort a mutable array by date. My array contains several dict with keys say:key1,key2,birthday.Now, I have to sort by its birthday key:
I know that this can be done using:
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"birthday" ascending:YES];
[myArray sortUsingDescriptors:[NSArray arrayWithObjects:descriptor,nil]];
But my problem is that I want to sort only those arrays, which don’t contain empty birthday field. My array will contains several empty birthday fields. I don’t want to sort those.
Finally I have to load these in table view through [self.mTable reloadData];.
First collect the indices of all objetcs without a birthday.
NSIndexSet *indexSet = [NSIndexSet indexSet];
[array enumerateObjectsUsingBlock:^(NSDictionary *dict, NSUInteger idx, BOOL *stop)
{
if(![[dict allKeys] containsObject:#"birthday"]){
[indexSet addIndex:idx];
}
}];
Now remove them from the original array
[array removeObjectsAtIndexes:indexSet];
Using a comparator block, sorting could look like
[array sortUsingComparator: ^(NSDictionary *d1, NSDictionary *d2) {
NSDate *date1 = [d1 objectForKey:#"birthday"];
NSDate *date2 = [d2 objectForKey:#"birthday"];
return [date1 compare:date2]
}
Create a different array to back your table view like this:
NSDictionary* obj1 = [NSDictionary dictionaryWithObject: [NSDate date] forKey: #"birthday"];
NSDictionary* obj2 = [NSDictionary dictionaryWithObject: [NSDate dateWithTimeIntervalSince1970: 0] forKey: #"birthday"];
NSDictionary* obj3 = [NSDictionary dictionaryWithObject: #"wow" forKey: #"no_birthday"];
NSArray* all = [NSArray arrayWithObjects: obj1, obj2, obj3, nil];
NSArray* onlyWithBirthday = [all valueForKeyPath: #"#unionOfObjects.birthday"];
And if you need the full objects for the table view, continue with this code:
NSPredicate* filter = [NSPredicate predicateWithFormat: #"SELF.birthday IN %#", onlyWithBirthday];
NSArray* datasource = [all filteredArrayUsingPredicate: filter];
Then you can apply your sort method of choice.

Using NSPredicate to filter based on multiple keys (NOT values for key)

I have the following NSArray containing NSDictionary(s):
NSArray *data = [[NSArray alloc] initWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:1], #"bill", [NSNumber numberWithInt:2], #"joe", nil],
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:3], #"bill", [NSNumber numberWithInt:4], #"joe", [NSNumber numberWithInt:5], #"jenny", nil],
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:6], #"joe", [NSNumber numberWithInt:1], #"jenny", nil],
nil];
I am wanting to create a filtered NSArray that only contains objects where the NSDictionary matches multiple 'keys' using NSPredicate.
For example:
filter the array to only contain the NSDictionary objects that have keys "bill" and "joe" [desired result: new NSArray would contain the first two NSDictionary objects]
filter the array to only contain the NSDictionary objects that have keys "joe" and "jenny" [desired result: new NSArray would contain the last two NSDictionary objects]
Can anyone please explain the format of the NSPredicate to achieve this?
Edit:
I can achieve a similar outcome to desired NSPredicate using:
NSMutableArray *filteredSet = [[NSMutableArray alloc] initWithCapacity:[data count]];
NSString *keySearch1 = [NSString stringWithString:#"bill"];
NSString *keySearch2 = [NSString stringWithString:#"joe"];
for (NSDictionary *currentDict in data){
// objectForKey will return nil if a key doesn't exists.
if ([currentDict objectForKey:keySearch1] && [currentDict objectForKey:keySearch2]){
[filteredSet addObject:currentDict];
}
}
NSLog(#"filteredSet: %#", filteredSet);
I'm imagining NSPredicate would be more elegant if one exists?
they only way I know is to combine two conditions like "'value1' IN list AND 'value2' IN list"
self.#allKeys should return all the keys of the dictionary (self is each dictionary in your array). If you don't write it with the prefix # then the dictionary will just look for a key that is "allKeys" instead of the method "- (NSArray*) allKeys"
The code:
NSArray* billAndJoe = [data filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"%# IN self.#allKeys AND %# IN self.#allKeys" , #"bill",#"joe" ]];
NSArray* joeAndJenny = [data filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"%# IN self.#allKeys AND %# IN self.#allKeys" , #"joe",#"jenny" ]]
Since a dictionary just returns nil if you ask for a value of a non-existing key, it is enough to specify that the value should be non-nil. A format like the following should cover your first case:
[NSPredicate predicateWithFormat: #"%K != nil AND %K != nil", #"bill", #"joe"]
The second case, with "joe" and "jenny" follows a similar pattern, of course.