ObjC: NSDictionary allKeysForObject *containing* (is this possible?) - objective-c

I am very new to objective-C and came across the NSDictionary method allKeysForObject:. Seems very useful. However, I have a NSDictionary which has several NSArrays (all of length 2) and which are keyed by NSStrings. Basically, the keys are items and the arrays define their two properties. If I wanted to pull all the item names that have a certain property, could this be done with something like allKeysForObject, or should I just loop over the dictionary and grow a mutable array (seems inefficient).
I'd include a code snippet, but I feel like this question is conceptual enough that code wouldn't really clarify anything. Oh, what the hell. Here's some simplified code:
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:[NSArray arrayWithObjects:[NSNumber numberWithInt:1],[NSNumber numberWithInt:2],nil],#"Car",[NSArray arrayWithObjects:[NSNumber numberWithInt:2],[NSNumber numberWithInt:3],nil],#"Boat",nil];
NSLog(#"%#",[dict allKeysForObject:???]); // this is the line I am not at all sure about.
EDIT: Thank you for the responses so far. I was not clear about my question, though. I am looking for a way to do something more general. I don't want to retrieve all keys for a particular object, say [1,2], but I want to look in the dictionary for all arrays including the NSNumber 1 and return those keys. So if I added #"Plane",[NSArray arrayWithObjects:[NSNumber numberWithInt:1],[NSNumber numberWithInt:3],nil], I'd like to somehow query for the NSNumber 1 and get #"Car" and #"Plane". I am getting the sense that this is not what this method was designed to do.

You are looking for -keysOfEntriesPassingTest:...
NSArray * selectedKeys = [dict keysOfEntriesPassingTest:^(id key, id obj, BOOL *stop)
{
return [obj containsObject:[NSNumber numberWithInt:2]];
}];

In your example, if you call
[dict allKeysForObject: [NSArray arrayWithObjects:
[NSNumber numberWithInt:2],
[NSNumber numberWithInt:3],
nil]]]
you should get an array containing just #"Boat"

Related

Create an NSMutableArray that contains several NSMutableDictionary objects

I've looked around and seen several postings concerning sorting NSMutableArray's that contain NSDictionaries, but haven't been able to find some concrete examples of how to actually make an NSMutableArray composed of NSDictionaries.
I am using NSUserDefaults to keep track of a user's preferences for several different items. The NSMutableArray will be used to keep track of the total items, and the NSDictionaries are for the individual preferences concerning each item.
I've written these methods to keep track of items in my preferences class, but I'm not too sure how to initialize the NMutableArray to contain NSDictionaries.
-(NSMutableArray *)deviceListArray
{
return [[NSUserDefaults standardUserDefaults] mutableArrayValueForKey:#"device_list_array"];
}
-(void) setDeviceListArray:(NSMutableArray *)deviceListArray
{
[[NSUserDefaults standardUserDefaults] setObject:deviceListArray forKey:#"device_list_array"];
}
If anyone could point me in the right direction, I would really appreciate it. Thank you very much! Or if someone has a better strategy for keeping track of items, each with different preferences, then that would be awesome too!
Thanks!!
Once you hand an object of to userDefaults, it owns that object - so you cannot go changing properties of dictionaries inside it.
What you can do is a two pass copy, where you have a local mutable array with mutable dictionaries in it. You would create a new mutable array of the same size, then for each mutable dictionary, call [NSDictionary dictionaryWithDictionary:], adding the new non-mutable dictionary to the new array.
If you have mutable objects buried in the dictionaries, well, you need to do the same for that.
All that said, if you have such a huge amount of stuff I suspect you have no properly thought of how to use the defaults system. I almost always just save strings, numbers, colors etc.
EDIT: code
NSMutableArray *array = [NSMutableArray arrayWithCapacity:10];
for(int i=0; i<10; ++i) {
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:10];
// add stuff to the dictionary
[array addObject:dict];
}
// later on want to change second dictionary:
NSMutableDictionary *dict = [array objectAtIndex:1];
// fool around with dict
// no need to re-save it in array, since its mutable
Not sure if this is what you were looking for:
#define kDeviceKey1 #"DeviceKey1"
#define kDeviceKey2 #"DeviceKey2"
#define kDeviceKey3 #"DeviceKey3"
#define kDeviceKey4 #"DeviceKey4"
#define kDeviceID #"DeviceID"
NSMutableArray *devices = [[NSMutableArray alloc]init];
NSMutableDictionary *deviceInfo = [[NSMutableDictionary alloc]init];
// create a dictionary record for earch device which contains thing...
// probably employ some kind of loop
deviceInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:1], kDeviceID,
// #"DeviceName1", kDeviceID, // or this
#"whateverthing1forDevice1", kDeviceKey1,
#"whateverthing2forDevice1", kDeviceKey2,
#"whateverthing3forDevice1", kDeviceKey3,
#"whateverthing4forDevice1", kDeviceKey4,
nil];
[devices addObject:deviceInfo];
// this is just an example, you would have some kind of loop to replace "whateverthing1forDevice1" with actual data
deviceInfo = [NSMutableDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:2], kDeviceID,
// #"DeviceName2", kDeviceID, // or this
#"whateverthing1forDevice2", kDeviceKey1,
#"whateverthing2forDevice2", kDeviceKey2,
#"whateverthing3forDevice2", kDeviceKey3,
#"whateverthing4forDevice2", kDeviceKey4,
nil];
[devices addObject:deviceInfo];
NSLog(#"devices: %#", devices);

NSMutableDictionary most efficient way to apply formula for each value

I have a NSMutableDictionary with NSNumbers. When I finish building the set I need to recalculate all the values using the currently stored value itself. Now I'm using fast enumeration and storing into a new NSMutableSet, but I'm not experienced in Objective C and there must be a more efficient way to do this:
for (id key in temp_target_results)
{
formula_score = MyFormula([[temp_target_results objectForKey:key] doubleValue]);
[target_results setObject:[NSNumber numberWithDouble:formula_score] forKey:key];
}
In the end I'm sorting by value (that's why I'm using NSMutableSet).
I don't know how much more anyone can help you optimize what you are doing because I don't know what's going on behind the scenes (outside of the context of the snippet of code you pasted above, or what's really going on inside your MyFormula function).
One optimization question I have would be: why are you storing everything as NSNumber objects anyways and not an array of doubles? The only advantage (that I can currently see) to doing that is if you're passing your NSNumber objects along in an NSArray, NSSet or NSDictionary that gets written out to disk or passed along in an NSNotification or.
But some of the things I would do would include getting rid of that extra, unnecessary call to objectForKey:
for (NSNumber * myNumber in temp_target_results)
{
formula_score = MyFormula([myNumber doubleValue]);
[target_results setObject:[NSNumber numberWithDouble:formula_score] forKey:[myNumber stringValue]];
}
Another consideration here:
Your target_results appears to not be a NSMutableSet because you are doing a NSMutableDictionary method of setObject:forKey:. If target_results really was a NSMutableSet, you'd only need to call:
[target_results addObject: [NSNumber numberWithDouble: formula_score]];

Sorting a mutable array using a dictionary key

I am trying to create a simple mutable array with a single key ("dayCounter") that I intend to use for sorting. I've read loads of examples on line, but no joy.
So I create this array. Note the first entry is a NSDictionary object. (The other objects are text)
cumArray = [NSMutableArray arrayWithObjects:[NSMutableArray arrayWithObjects:
[NSDictionary dictionaryWithObject:[NSString stringWithFormat:#"%i", dayCounter] forKey:#"dayCounter"],[[dailyArray objectAtIndex:x]objectAtIndex:0],[[dailyArray objectAtIndex:x]objectAtIndex:1],[[dailyArray objectAtIndex:x]objectAtIndex:2], nil],nil];
I save the array in a plist and everything looks great after the load.
However, when I come to sort the array, the program crashes. I have tried every combination of the following:
NSSortDescriptor *aSortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"dayCounter" ascending:YES];
[cumArray sortUsingDescriptors:[NSArray arrayWithObject:aSortDescriptor]];
Do I need a dictionary item to act as a key? Can I sort on the first object any easier? Any help is much appreciated.
Sometimes using too many nested expressions can obscure what's really going on. For example, the 'simple' mutable array you created actually contains a nested mutable array, rather than directly containing the dictionaries you're trying to sort.
So instead of this:
cumArray = [NSMutableArray arrayWithObjects:[NSMutableArray arrayWithObjects:
[NSDictionary dictionaryWithObject:[NSString stringWithFormat:#"%i", dayCounter] forKey:#"dayCounter"],[[dailyArray objectAtIndex:x]objectAtIndex:0],[[dailyArray objectAtIndex:x]objectAtIndex:1],[[dailyArray objectAtIndex:x]objectAtIndex:2], nil],nil];
try doing this
NSDictionary *dict1 = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:#"%i", dayCounter]
forKey:#"dayCounter"]
NSArray *objs = [dailyArray objectAtIndex:x];
NSDictionary *dict2 = [objs objectAtIndex:0];
NSDictionary *dict3 = [objs objectAtIndex:1];
NSDictionary *dict4 = [objs objectAtIndex:2];
// Note: You might want to temporarily log the values of dict2 - 4 here to make sure they're
// really dictionaries, and that they all actually contain the key 'dayCounter'.
cumArray = [NSMutableArray arrayWithObjects:dict1, dict2, dict3, dict4, nil];
Assuming that you really have a mutable array of dictionaries, each of which contains the key dayCounter, the sort descriptor you showed in your example should work just fine.
Your setup makes no sense. You are saying yourself that only the first object in the array is a dictionary that contains the key `#"dayCounter" ("The other objects are text"). How is it supposed to be sorted if only one object contains the sort criteria?
You need to sort the array with a method, like - (NSComparisunResult)compareDict
If you have to compare 2 dictionaries and determine which one should be ordered above the other ( NSOrderedAscending ) then you need to "extend" NSDictionary:
#interface NSDictionary (SortingAdditions) {}
- (NSComparisonResult)compareTo:(NSDictionary *)other;
#end
#implementation NSDictionary (SortingAddictions)
- (NSComparisonResult)compareTo:(NSDictionary *)other
{
if( [self count] > [other count] )
{ return NSOrderedAscending; }
}
#end
This method will sort NSDictionaries according to the amount of objects that they contain.
Other values you can return here are: NSOrderedDescending and NSOrderedSame.
Then you can sort the mutable array with:
[SomeMutableArray sortUsingSelector:#selector(compareTo:)];
Keep in mind that every object in the array will need to be an NSDictionary, otherwise you will get an exception: unrecognized selector sent to instance blabla
You can do the same thing for any type of object, if the array contains both NSStrings, NSNumbers and NSDictionaries you should take a different approach

objective-c how to create an enumeration array with its string values as keys?

i have an enumeration say gender, now i want to associate it to string values to use in the view inside a picker view. It's cocoa-touch framework and objective-c as language.
So i don't know of a way to set the data source of the picker view as the enumeration, as could have been done in other frameworks. So i've been told i have to make array of enum values. and then i tried to add thos into an NSMutableDictionary with their respective string values.
So i ended up with
NSArray* genderKeys = [NSArray arrayWithObjects:#"Male",#"Female",nil] ;
NSArray* genderValues = [NSArray arrayWithObjects:[NSNumber numberWithInt:male],[NSNumber numberWithInt:female],nil];
for(int i =0;i<[genderKeys count];i++)
[_genderDictionary setValue:[genderValues objectAtIndex:i] forKey:[genderKeys objectAtIndex:i]];
and it's not working saying it's not a valid key, and i've read the key-coding article and i know now what's key and whats keypath, but still how can i solve that. It's ruining my life, Please help.
Sorry guys, i was using NSDictionary for _genderDictionary.But i had in my mind that it was nsmutable. Thank you all.
Be careful using UI text as keys into your database. What amount when you need to localise your application to french, chinese, arabic etc?
That works for me. Running this (your code, with the first line added so it would compile) seems to work fine.
NSMutableDictionary *_genderDictionary = [NSMutableDictionary dictionary];
NSArray* genderKeys = [NSArray arrayWithObjects:#"Male",#"Female",nil] ;
NSArray* genderValues = [NSArray arrayWithObjects:[NSNumber numberWithInt:1],[NSNumber numberWithInt:2],nil];
for(int i =0;i<[genderKeys count];i++)
[_genderDictionary setValue:[genderValues objectAtIndex:i] forKey:[genderKeys objectAtIndex:i]];
NSLog()-ing _genderDictionary outputs this
{
Female = 2;
Male = 1;
}
edit: re-reading your question, makes me think what you are looking for is the delegate methods of UIPickerView... implementing –pickerView:titleForRow:forComponent: is where you set the text that appears in the picker. If you have an NSArray of genders, you would do something like return [_genderArray objectAtIndex:row]; That way you don't need to fuss around with a dictionary and keys.
edit 2: a picker's datasource can't be an NSArray or NSDictionary directly. It has to be an object that implements UIPickerView's datasource/delegate protocol (which I suppose you could do with a subclass of NSArray, but that'd be cah-ray-zay!).
If I understand you correctly, you try to create a pre-populated dictionary.
You could use [NSDictionary dictionaryWithObjectsAndKeys:] for that.
[NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithUnsignedInt:0], #"Male",
[NSNumberWithUnsignedInt:1], #"Female", nil]

Bidirectional Map in Cocoa

Cocoa provides NSDictionary, which essentially is an associative array.
Is there a nice way to get bidirectional associativity? i.e. one way would have been if NSDictionary had a keyForObject: method which mirrored the behavior of objectForKey:.
I don't really care if NSDictionary is not the way to get this. I know NSDictionary does provide keysOfEntriesPassingTest: but it returns an NSSet * which doesn't look very clean for the kind of thing I want to have.
Here is an example:
fieldMap = [[NSDictionary alloc] initWithObjectsAndKeys:
#"c0",#"synckey",
#"c1",#"wi_id",
#"c2",#"wi_parent_id",
#"c3",#"wi_type",
#"c4",#"wi_rh_task",
#"c5",#"decision_key",
#"c6",#"wi_stat",
#"c7",#"wi_prio",
#"c8",#"wi_cd",
#"c9",#"wi_ct",
#"cb",#"wi_dh_stat",
#"cc",#"wi_user",
nil];
I want to translate between c0 and synckey and back, and ditto for the other fields.
The closest thing for what you're after is, I believe allKeysForObject:. This returns an NSArray containing the keys corresponding to all occurrences of a given object in the dictionary. Obviously if the object is in the dictionary only once, the NSArray will contain only one object.
So with your example:
fieldMap = [[NSDictionary alloc] initWithObjectsAndKeys:
#"c0",#"synckey",
#"c1",#"wi_id",
#"c2",#"wi_parent_id",
#"c3",#"wi_type",
#"c4",#"wi_rh_task",
#"c5",#"decision_key",
#"c6",#"wi_stat",
#"c7",#"wi_prio",
#"c8",#"wi_cd",
#"c9",#"wi_ct",
#"cb",#"wi_dh_stat",
#"cc",#"wi_user",
nil];
This additional code would return an array containing 1 string object evaluating to #"c7":
NSArray *keyArray = [fieldMap allKeysForObject:#"wi_prio"];
[Aside: Note that this would only work here because of how the compiler works; it takes all occurences of #"wi_prio" and makes them the same object. If instead you had perhaps loaded the dictionary from disk etc, this approach will not work for NSStrings. Instead you should probably use allKeys and then iterate through them, comparing with [mystring isEqualToString:anotherString].]
The CHDataStructures framework has CHBidirectionalDictionary.