Filter NSArray with 2D-NSDictionaries using NSPredicate - objective-c

i have a NSArray containing some NSDictionaries which themselves also include a NSDictionary.
NSDictionary *dict1 = [NSDictionary dictionaryWithObject:[NSDictionary dictionaryWithObject:#"cover" forKey:#"type"] forKey:#"image"];
NSDictionary *dict2 = [NSDictionary dictionaryWithObject:[NSDictionary dictionaryWithObject:#"cover" forKey:#"type"] forKey:#"image"];
NSDictionary *dict3 = [NSDictionary dictionaryWithObject:[NSDictionary dictionaryWithObject:#"back" forKey:#"type"] forKey:#"image"];
NSDictionary *dict4 = [NSDictionary dictionaryWithObject:[NSDictionary dictionaryWithObject:#"cover" forKey:#"type"] forKey:#"image"];
NSArray *myArray = [NSArray arrayWithObjects:dict1, dict2, dict3, dict4, nil];
is there a way to filter myArray for all Image-Dictionaries where the type is f.e. "cover" using a NSPredicate?
tried predicates like
predicateWithFormat:#"(SELF.image.type == %#)", #"cover"]
or
predicateWithFormat:#"(image.type == %#)", #"cover"]
but without success.
thanks in advance! leave a comment if something is unclear
// edit
so
NSPredicate *p = [NSPredicate predicateWithFormat:#"image.type == %#", #"cover"];
is working. but in my case i want to sort out size == original. what i did is
NSPredicate *p = [NSPredicate predicateWithFormat:#"image.size == %#", #"original"];
but then my app crashes with
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[NSSymbolicExpression length]: unrecognized selector sent to instance
and pointing to my filteredArrayUsingPredicate method. i cannot see any difference between type and size. see my NSLog of my Array here
(
{
image = {
height = 1500;
id = 4e5808765e73d607350059b4;
size = original;
type = poster;
url = "someurl";
width = 1000;
};
},
{
image = {
height = 750;
id = 4e5808765e73d607350059b4;
size = mid;
type = poster;
url = "someurl";
width = 500;
};
},
anybody knows why it crashes when i try to use size instead of type ?

SIZE is a reserved keyword.
Found at the bottom of the Predicate Programming Guide:
The following words are reserved:
AND, OR, IN, NOT, ALL, ANY, SOME, NONE, LIKE, CASEINSENSITIVE, CI, MATCHES, CONTAINS, BEGINSWITH, ENDSWITH, BETWEEN, NULL, NIL, SELF, TRUE, YES, FALSE, NO, FIRST, LAST, SIZE, ANYKEY, SUBQUERY, CAST, TRUEPREDICATE, FALSEPREDICATE

The following code worked for me:
NSDictionary *dict1 = [NSDictionary dictionaryWithObject:[NSDictionary dictionaryWithObject:#"cover" forKey:#"type"] forKey:#"image"];
NSDictionary *dict2 = [NSDictionary dictionaryWithObject:[NSDictionary dictionaryWithObject:#"cover" forKey:#"type"] forKey:#"image"];
NSDictionary *dict3 = [NSDictionary dictionaryWithObject:[NSDictionary dictionaryWithObject:#"back" forKey:#"type"] forKey:#"image"];
NSDictionary *dict4 = [NSDictionary dictionaryWithObject:[NSDictionary dictionaryWithObject:#"cover" forKey:#"type"] forKey:#"image"];
NSArray *myArray = [NSArray arrayWithObjects:dict1, dict2, dict3, dict4, nil];
NSPredicate *p = [NSPredicate predicateWithFormat:#"image.type == %#", #"cover"];
NSLog(#"%#", [myArray filteredArrayUsingPredicate:p]);

Related

NSDictionary dictionaryWithObjectsAndKeys vs literal notation

What are the differences between using NSDictionary/NSArray constructors and the literal notation?
NSDictionary *dict1 = [NSDictionary dictionaryWithObjectsAndKeys:#"bar", #"foo"];
NSDictionary *dict2 = #{ #"foo" : #"bar" };
NSArray *arr1 = [NSArray arrayWithObject:#"one", #"two"];
NSArray *arr2 = #[ #"one", #"two" ];
What about accessing dictionary and array elements?
NSLog(#"%#", [dict1 objectForKey:#"foo"]);
NSLog(#"%#", dict2[#"foo"]);
NSLog(#"%#", [arr1 objectAtIndex:0]);
NSLog(#"%#", arr2[0]);
Is the difference purely readability, or is there a performance/behavior difference as well?
As explained in the Clang documentation, the literal #{} and #[] forms are identical to dictionaryWithObjects:forKeys:count: and arrayWithObjects:count:, which verify that no nil values are present.
Similarly, the subscript notations translate directly to objectAtIndexedSubscript:/setObject:atIndexedSubscript: and objectForKeyedSubscript:/setObject:forKeyedSubscript: (which can be implemented for your own classes if you so desire).
Compiling this code…
#import Foundation;
int main() {
NSDictionary *dict1 = [NSDictionary dictionaryWithObjectsAndKeys:#"bar", #"foo", nil];
NSDictionary *dict2 = #{#"foo" : #"bar"};
NSString *result1 = dict2[#"bar"];
NSArray *arr1 = [NSArray arrayWithObjects:#"one", #"two", nil];
NSArray *arr2 = #[#"one", #"two"];
NSString *result2 = arr2[1];
return 0;
}
…and opening the binary with Hopper reveals this pseudocode, which is not perfect, but good enough to see what's going on:

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

NSPredicate 'OR' filtering based on an NSArray of keys

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.

How do you change the elements within an NSArray?

I am a bit confused as to how arrays are handled in Objective-C.
If I have an array such as
NSarray *myArray = [[NSArray alloc]
initWithObjects:#"N", #"N", #"N", #"N", #"N",
nil];
how do I change the first occurrence to "Y"?
You need an NSMutableArray ..
NSMutableArray *myArray = [[NSMutableArray alloc]
initWithObjects:#"N", #"N", #"N", #"N", #"N",
nil];
and then
[myArray replaceObjectAtIndex:0 withObject:#"Y"];
You can't, because NSArray is immutable. But if you use NSMutableArray instead, then you can. See replaceObjectAtIndex:withObject::
[myArray replaceObjectAtIndex:0 withObject:#"Y"]
Write a helper method
-(NSArray *)replaceObjectAtIndex:(int)index inArray:(NSArray *)array withObject:(id)object {
NSMutableArray *mutableArray = [array mutableCopy];
mutableArray[index] = object;
return [NSArray arrayWithArray:mutableArray];
}
Now you can test this method with
NSArray *arr = #[#"a", #"b", #"c"];
arr = [self replaceObjectAtIndex:1 inArray:arr withObject:#"d"];
logObject(arr);
This outputs
arr = (
a,
d,
c
)
You can use similar method for NSDictionary
-(NSDictionary *)replaceObjectWithKey:(id)key inDictionary:(NSDictionary *)dict withObject:(id)object {
NSMutableDictionary *mutableDict = [dict mutableCopy];
mutableDict[key] = object;
return [NSDictionary dictionaryWithDictionary:mutableDict];
}
You can test it with
NSDictionary *dict = #{#"name": #"Albert", #"salary": #3500};
dict = [self replaceObjectWithKey:#"salary" inDictionary:dict withObject:#4400];
logObject(dict);
which outputs
dict = {
name = Albert;
salary = 4400;
}
You could even add this as a category and have it easily available.