From array of dictionaries, make array containing values of one key - objective-c

I have an array of dictionaries. I would like to extract an array with all the elements of one particular key of the dictionaries in the original array. Can this be done without enumeration?

Yes, use the NSArray -valueForKey: method.
NSArray *extracted = [sourceArray valueForKey:#"a key"];

Yes, just use Key-Value Coding to ask for the values of the key:
NSArray* names = [NSArray arrayWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:
#"Joe",#"firstname",
#"Bloggs",#"surname",
nil],
[NSDictionary dictionaryWithObjectsAndKeys:
#"Simon",#"firstname",
#"Templar",#"surname",
nil],
[NSDictionary dictionaryWithObjectsAndKeys:
#"Amelia",#"firstname",
#"Pond",#"surname",
nil],
nil];
//use KVC to get the names
NSArray* firstNames = [names valueForKey:#"firstname"];
NSLog(#"first names: %#",firstNames);

Related

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.

Which type I should use to save many objects with same key? (iOS)

Which type I should use to save many objects with same key?
I should post data to server where one of parameter is suggestedTo and it contains userId.
This parameters should be more then one. So I'm confused which data type I should use to save them.
For example array or dictionary should looks like
{
#"suggestedTo" = 111,
#"suggestedTo" = 222,
#"suggestedTo" = 333,
etc.
}
This is typically handled with a dictionary of sets (or arrays if the data is ordered). So in this case, you'd have something like:
NSSet *suggestedTo = [NSSet setWithObjects:[NSNumber numberWithInt:111],
[NSNumber numberWithInt:222],
[NSNumber numberWithInt:333], nil];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:suggestedTo,
#"suggestedTo", nil];
You could use a dictionary of arrays
NSArray *suggestedTos = [[NSArray alloc] initWithObjects:
[NSNumber numberWithInt:111],
[NSNumber numberWithInt:222],
[NSNumber numberWithInt:333], nil];
NSDictionary *myDictionary = [[NSDictionary alloc] initWithObjectsAndKeys:
suggestedTos, #"suggestedTo", nil];

Why can I pull one string out of an NSDictionary, but not another?

I have an NSDictionary and I am trying to pull a string out of it. For some reason, the last string seems irretrievable(!?!). In the code below, I retrieve the NSString object for labelString, with no problem at all. But when I try to retrieve the NSString for foo, I always get nil. But I don't see the difference - can you see what I'm doing wrong?
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:CellStyleLabelledStringCell], #"cellStyle",
#"name", #"fieldName",
#"Name", #"labelString",
foodItem.name, #"contentString",
#"foo", #"fookey",
nil];
NSString *string1 = (NSString *)[dict objectForKey:#"fookey"];
NSString *string2 = (NSString *)[dict objectForKey:#"labelString"];
NSLog(#"[%#][%#]", string1, string2);
The log message looks like this, and backs-up what I'm seeing in the debugger (i.e., string1 is null):
2012-03-17 21:35:03.302 QuickList7[8244:fb03] [(null)][Name]
Truly perplexed. Thanks in advance.
foodItem.name is nil, so -[NSDictionary dictionaryWithObjectsAndKeys:] stops there, and doesn't add the subsequent objects to the dictionary.
In other words, it's as if you did this:
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:CellStyleLabelledStringCell], #"cellStyle",
#"name", #"fieldName",
#"Name", #"labelString",
nil];
This is why you have to be careful with any method that takes nil as an "end of arguments" sentinel.
Kurt's answer is correct, foodItem.name is nil.
To prevent that, you can either always check objects to see if they're nil before adding to a dictionary, or use the following macro to replace all nil items with NSNull objects:
#define n2N(value) (value ? value : [NSNull null])
So using that macro, your code above would look like:
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:CellStyleLabelledStringCell], #"cellStyle",
#"name", #"fieldName",
#"Name", #"labelString",
n2N(foodItem.name), #"contentString",
#"foo", #"fookey",
nil];
Also, there's no need to typecast the result of objectForKey: to NSString since that method returns id.

Objective-c SBJSONWriter convert array of dictionaries to JSON

I'm having some trouble with SBJsonWriter at the moment.
I need to send a request that contains a json object of name/value pairs. e.g.
[{%22uid%22:1,%22version%22:1}]
I can't figure out how to do this in Obj-C with the SBJson Writer framework.
For each pair I have tried to construct a dictionary then add the dictionary to an array. This results in an array containing many dictionaries, each containing one name/value pair.
Any idea on how a fix for this or is it possible?
Thanks in advance
To produce an Objective-C structure equivalent to the above JSON you should do this:
NSArray* json = [NSArray arrayWithObject: [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt: 1], #"uid",
[NSNumber numberWithInt: 1], #"version",
nil]];
Check my answer to the '' SBJsonWriter Nested NSDictionary '' question. it illustrates how to properly use SBJsonWriter.
It includes error check and some pieces of advise about SBJsonWriter behaviour with NSDate, float, etc..
Excerpt:
NSDictionary* aNestedObject = [NSDictionary dictionaryWithObjectsAndKeys:
#"nestedStringValue", #"aStringInNestedObject",
[NSNumber numberWithInt:1], #"aNumberInNestedObject",
nil];
NSArray * aJSonArray = [[NSArray alloc] initWithObjects: #"arrayItem1", #"arrayItem2", #"arrayItem3", nil];
NSDictionary * jsonTestDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
#"stringValue", #"aString",
[NSNumber numberWithInt:1], #"aNumber",
[NSNumber numberWithFloat:1.2345f], #"aFloat",
[[NSDate date] description], #"aDate",
aNestedObject, #"nestedObject",
aJSonArray, #"aJSonArray",
nil];

NSMutable Dictionary adding objects

Is there a more efficient way to add objects to an NSMutable Dictionary than simple iteration?
Example:
// Create the dictionary
NSMutableDictionary *myMutableDictionary = [NSMutableDictionary dictionary];
// Add the entries
[myMutableDictionary setObject:#"Stack Overflow" forKey:#"http://stackoverflow.com"];
[myMutableDictionary setObject:#"SlashDotOrg" forKey:#"http://www.slashdot.org"];
[myMutableDictionary setObject:#"Oracle" forKey:#"http://www.oracle.com"];
Just curious, I'm sure that this is the way it has to be done.
NSDictionary *entry = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithDouble:acceleration.x], #"x",
[NSNumber numberWithDouble:acceleration.y], #"y",
[NSNumber numberWithDouble:acceleration.z], #"z",
[NSDate date], #"date",
nil];
If you have all the objects and keys beforehand you can initialize it using NSDictionary's:
dictionaryWithObjects:forKeys:
Of course this will give you immutable dictionary not mutable. It depends on your usage which one you need, you can get a mutable copy from NSDictionary but it seems easier just to use your original code in that case:
NSDictionary * dic = [NSDictionary dictionaryWith....];
NSMutableDictionary * md = [dic mutableCopy];
... use md ...
[md release];
Allow me to add some information to people that are starting.
It is possible to create a NSDictionary with a more friendly syntax with objective-c literals:
NSDictionary *dict = #{
key1 : object1,
key2 : object2,
key3 : object3 };
NSMutableDictionary *example = [[NSMutableDictionary alloc]initWithObjectsAndKeys:#5,#"burgers",#3, #"milkShakes",nil];
The objects come before the keys.