take nsdictionary and nsstring values from a nsdictionary - objective-c

I have a function that returns an NSDictionary named data. It contains 2 objects: a NSDictionary object with the key currency_data and an NSString object with the key time.
I want to pass :
the string value with the key time to a new NSString object
the NSDictionary object with the key currency_data to a NSMutableDictionary variable.
How can I do that?

Depending on whether you use ARC. With ARC your example should work, without ARC you need to retain at least the string values.
NSDictionary *dataDict = [foo data];
NSMutableDictionary *currency_dict= [[NSMutableDictionary alloc] initWithDictionary:dataDict[#"currency_data"]];
NSString *time = [data[#"time"] retain];
What anoop-vaidy meant is I think, if you need a mutable dictionary, create that in the data call directly and pass it out. Another note: You can use your knowledge of you data structure in a better way. Instead of building a dictionary with 2 keys, use the first value ( the time string) as the key and the second (the currency dict) as the value. You can access values and keys in dictionaries quite easily
NSArray *value = dict.allValues;
NSArray *keys = dict.allKeys;

Related

Is a NSMutableDictionary in a NSDictionary still mutable?

The question is as simple as the title:
Is a NSMutableDictionary in a NSDictionary still mutable? Is the mdict mutable below?
NSMutableDictionary *mdict = [[NSMutableDictionary alloc] init];
NSDictionary *dict = [[NSDictionary alloc] initWithObjectsAndKeys:mdict, #"key", nil];
And, is a NSDictionary in a NSMutableDictionary still immutable?
Further, what if it's array/set instead of dictionary?
Absolutely! Mutability of an object does not change when you place it into a container.
When you place a mutable dictionary into another collection, mutable or immutable, that collection adds a reference to the mutable dictionary object, but it does not change it in any other way. Same goes for placing immutable objects into collections: collections reference these objects without changing their nature.
This remains true while your object is in memory. If you serialize it and then deserialize it back, the process of deserialization may remove mutability. For example, if you save NSMutableDictionary into NSUserDefaults and then read it back, you would get back an immutable dictionary.
Yes. Objects generally don't know when they're placed into a collection, so they can't change their behavior based on that. NSDictionary does copy its keys (precisely so you can change the original object without affecting the dictionary), but it just stores a normal reference to the value.
As long as you access your variables like so
NSMutableDictionary * tempDict = [mdict objectForKey: #"Key"];
NSMutableDictionary * tempDict2 = [arrayVar objectAtIndex: index];
The temp variables retain all the functionality as before

NSDictionary assign variable from key in loop

I've got an NSDictionary loop like this:
NSMutableDictionary *readDict = [[NSMutableDictionary alloc] initWithContentsOfFile:plist];
NSDictionary *keys = [readDict objectForKey:data]; //Dictionary
for(NSString *object in keys) {
NSData *(current object name) = [keys objectForKey:object]; //Dictionary
}
// Can I assign ^ the object name to a new NSData var as it loops?
Is it possible to assign a variable name from the object name as it loops?
Objective-C is basically C with a few extensions. In your vast knowledge of the C language, have you ever seen a way to create variables at runtime? I haven't. So if I figured out correctly what you want to do, you can't.
And what is "data"? Is it a variable containing a string? The line
NSDictionary *keys = [readDict objectForKey:data]; //Dictionary
assumes that the dictionary readDict has one key-value pair where the key is equal to the string contained in the variable data, and the value is an NSDictionary. Is that what you wanted?
Next, a for loop running over a dictionary will run over the keys in that dictionary. So in
for(NSString *object in keys)
the name "object" is badly chosen, because it is actually a key.
Usually NSDictionary will either contain keys with know values, and you'd never think of using a for-loop to access them. Or you have a dictionary that can contain any number of unknown keys and you want to process them one after the other. You wouldn't have different variables.

Can NSDictionary contain different types of objects as values?

Why does the following result in a BAD_ACCESS error?
NSDictionary *header=[[NSDictionary alloc]initWithObjectsAndKeys:#"fred",#"title",1,#"count", nil];
Can you have different types of objects as values in NSDictionary, including another NSDictionary?
You can put any type of object into an NSDictionary. So while #"fred" is OK, 1 is not, as an integer is not an object. If you want to put a number in a dictionary, wrap it in an NSNumber:
NSDictionary *header = { #"title": #"fred", #"count": #1 };
Not the way you have it. The number 1 is a primitive and the NSArray object can hold only objects. Create a NSNumber for the "1" and then it will store it.
An NSDictionary can only contain Objective-C objects in it (such as NSString and NSArray), it cannot contain primitive types like int, float, or char*. Given those constraints, heterogeneous dictionaries are perfectly legal.
If you want to include a number such as 1 as a key or value, you should wrap it with an NSNumber:
NSDictionary *header=[[NSDictionary alloc] initWithObjectsAndKeys:
#"fred", #"title",
[NSNumber numberWithInt:1], #"count",
nil];
The only requirement is that is be an object. It's up to you to handle the objects properly in your code, but presumably, you can keep track of their types based on the keys.
1 is not an object. If you want t o put a number into a dictionary you may want to convert it to an NSNumber.

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

Is it possible to invoke NSDictionary's valueForKeyPath: when a key contains periods?

I'm trying to get the value of the repeatInterval key in the com.apple.scheduler plist. I'd like to just use NSDictionary's valueForKeyPath: method like so:
CFPropertyListRef value;
value = CFPreferencesCopyValue(CFSTR("AbsoluteSchedule"),
CFSTR("com.apple.scheduler"),
kCFPreferencesCurrentUser,
kCFPreferencesCurrentHost);
NSNumber *repeatInterval = [(NSDictionary *)value valueForKeyPath:#"com.apple.SoftwareUpdate.SUCheckSchedulerTag.Timer.repeatInterval"];
But the problem with this is that the first key is really "com.apple.SoftwareUpdate", not just "com". I can get around this by getting that first value separately:
NSDictionary *dict = [(NSDictionary *)value valueForKey:#"com.apple.SoftwareUpdate"];
NSNumber *repeatInterval = [dict valueForKeyPath:#"SUCheckSchedulerTag.Timer.repeatInterval"];
I just wanted to know if there is a way to escape periods in a keypath so I can eliminate this extra step.
NSDictionary doesn't have a valueforKeyPath: method. It just invokes the NSObject implementation which is the root of your problem.
Maybe you could reimplement it in a category on NSDictionary with your own escape characters.