How can we identify the object is available for a particular key. I have tried following:
for(NSDictionary *item in jsonArray){
if([item objectForKey:#"EventDate"])
NSLog([item objectForKey:#"EventDate"]);
}
This is getting crash the code with error:
-[__NSCFString objectForKey:]: unrecognized selector sent to instance 0x6a567b0
I have also find many posts that is showing objectForKey will return nil if a key doesn't exists. Than my question is there is also a method in NSDictionary class that is "setNilValueForKey". How is this possible that we cannot specify the NSDictionary key with nil object and also we have the method to set nil value for object in NSDictionary.
Please Suggest on first and also make me clear on second query.
1) Your jsonArray contains other types of objects than NSDictionaries, including at least one NSString. NSString doesn't respond to objectForKey: so it throws an exception when you try to call it. You'll have to look at the JSON to determine how to proceed with whatever you were doing.
2) There is an NSObject method setNilValueForKey: which is related to key-value coding. This isn't really related to NSDictionary. If you really need to represent nil in your dictionary, set [NSNull null] as the object for your key that represents nil.
Hope this helps!
Not all the objects in your array are dictionaries. You need to check what kind of data you're processing before you work on it. Something like this:
for(NSObject* item in jsonArray) {
if ([item isKindOfClass:[NSDictionary class]]) {
// do dictionary stuff
}
else if ([item isKindOfClass:[NSString class]]) {
// do string stuff
}
}
item is not a NSDictionary its a String. So check your jsonArray it may be contains only strings not dictinaries
Answer to second query
There is a non-nil object called NSNull that is built specifically to represent nils in situations where "plain" nil is not acceptable. If you replace your nils with [NSNull null] object, NSDictionary will take them. You would need to check for NSNull on the way out, though.
Refer more here
Answer to first query
for(id *item in jsonArray)
{
if([item isKindofClass:[NSDictionary class])
{
NSArray *allKeys = [item allKeys];
for(NSString *strKey in allKeys)
{
if([strKey isEqualToString:#"EventDate"])
{
// EventDate key has object
NSLog([item objectForKey:#"EventDate"]);
}
}
}
}
Related
Given this P-List Dictionary:
How do I get at the 3rd. Key - "Dinner" - which in itself is also a Dictionary, and parse its values correctly?
Or, should I structure this P-List differently to begin with, so I can get at everything more easily?
Here's what I got, starting by grabbing all the Keys from my 'MenuDictionary' and storing them in an Array:
// Load the Property-List file into the Dictionary:
MenuDictionary = [[NSDictionary alloc] initWithContentsOfFile:menuPath];
// Get all the Keys from the Dictionary, put 'em into a 'mealTimesArray':
mealTimesArray = [[MenuDictionary allKeys] sortedArrayUsingSelector:#selector(compare:)];
// For each meal-type KEY, grab all the Values (dishes) in it and store them in a 'mealDishesArray':
for (NSString *mealTime in mealTimesArray) {
NSArray *mealDishesArray = [MenuDictionary valueForKey:mealTime];
// Now I can iterate through the 'mealDishesArray' to access each dish at
// a time, so I can print them out or do whatever else:
for (NSString *dish in mealDishesArray) {
NSLog(#"Iterating through 'mealDishesArray' now...");
NSLog(#"Current 'dish' is: %#", dish);
The problem occurs when I get to the "Dinner" key: its a Dictionary, containing 2 Keys with 2 array Values. So how do I load its contents into a Dictionary object? More specifically, what 'init' method should I be using to load the "Dinner" contents into my new Dictionary object?
I tried this - doesn't work:
// I put this inside the first fast-enum loop:
if ([mealTime isEqualToString: #"Dinner"]) {
// init new Dictionary object (declared previously):
dinnerDictionary = [[NSDictionary alloc] initWith ???];
I'd like to init it with the contents of the "Dinner" Key, but its not a P-List file obviously, so I can't use
initWithContentsOfFile: pathName
I don't understand which of the other init methods will give me access to both the Keys and Values of "Dinner". Because even though "Dinner" is structured as a Dictionary, its currently sitting inside an Array, which doesn't regard it as a Dictionary (I think...)
I'm a little unclear about this obviously.
Or, should I be structuring my P-List differently to begin with so I can get at this nested Dinner dictionary?
Any ideas?
I think plist structure makes sense, and dealing with the contents conditionally based on class is perfectly okay, too. I would react to what's in the plist within a reasonable range of expectations, so...
// for each meal...
for (NSString *mealTime in mealTimesArray) {
// we're not sure what kind of meal we have
id mealInfo = [MenuDictionary valueForKey:mealTime];
if ([id isKindOfClass:[NSArray self]]) {
// it's an array? cast as an array and deal with the array
NSArray *mealDishesArray = (NSArray *)mealInfo;
[self handleMealArray:mealDishesArray];
} else if ([id isKindOfClass:[NSDictionary self]]) {
// it's a dictionary? that's cool, too. cast as a dictionary and deal with it
NSDictionary *mealDictionary = (NSDictionary *)mealInfo;
[self handleMealDictionary:mealDictionary];
}
}
// you've worked out to handle the array
- (void)handleMealArray:(NSArray *)mealDishesArray {
for (NSString *dish in mealDishesArray) {
NSLog(#"Iterating through 'mealDishesArray' now...");
NSLog(#"Current 'dish' is: %#", dish);
}
}
// handle the dictionary like a dictionary, realizing that it contains
// arrays, which you've already worked out how to handle
- (void)handleMealDictionary:(NSDictionary *)mealDictionary {
for (NSString *dishType in [mealDictionary allKeys]) {
NSArray *mealDishesArray = [mealDictionary valueForKey:dishType];
[self handleMealArray:mealDishesArray];
}
}
The 'problem' is with this line:
NSArray *mealDishesArray = [MenuDictionary valueForKey:mealTime];
when mealTime is 'Dinner' you are assigning mealDishesArray a value that is an NSDictionary. Thinking you have an array you then use:
for (NSString *dish in mealDishesArray)
to iterate over the elements in the array which is not going to give you what you expect for 'Dinner'. You might consider adding something like:
NSAssert ([mailDishesArray isKindOfClass: [NSArray class]], #"Expecting an array");
after your assignment to mealDishesArray.
What is the solution? Your PLIST has a totally different structure between 'Breakfast,' 'Lunch,' and 'Dinner.' Why is 'Dinner' a NSDictionary and the others are NSArray? Make them all the same type. If they can't be, then you must conditionalize your code based on:
if ([mealTime isEqualToString: #"Dinner"]) {
NSDictionary *dinnerDictionary = (NSDictionary *) [MenuDictionary valueForKey:mealTime];
/* ... */ }
You don't need to alloc anything or read anything from file; you already have a dictionary for the 'Dinner' data.
For example, I have #"John", #"Peter", ..., #"May" and need to construct NSArray:
[#"John", #"Peter", ..., #"May"]
The number of NSString is unknown and is taking from an import text file. As NSArray does not support appending new element, how can I create NSArray?
Thanks
UPDATE, let me rephrase the question. How can I create the dynamic array paremeter required by the follow function call?
[segmentedAttributes attributesWithTitlesArray:[NSArray arrayWithObjects:#"John", #"Peter", #"May", nil]]
You don't.
You misunderstand the library behavior.
It is true that there is a convenience constructor arrayWithObjects: which is used thus:
NSArray* array=[NSArray arrayWithObjects:#"Low", #"Medium", #"High", nil];
But this does not create an array with nil at the end. This nil is just to signify the end of the variable-length argument list. It just creates an NSArray with three elements, not four with the last one nil.
You just need to create an NSArray containing the required elements, and pass it to the library function. For example:
NSMutableArray*array=[NSMutableArray array];
while(...){
... get a string ...
[array addObject: string];
}
SCSegmentedAttributes*attributes=[SCSegmentedAttributes attributesWithSegmentTitlesArray:array];
should work, without adding a nil or [NSNull null].
You can't store nil in a Foundation collection class. Instead, you can use [NSNull null]. Use an NSMutableArray, then when you want to add your 'nil' object, use [NSNull null].
Note that when you want to see if an object is [NSNull null] later on, that method will return the same instance every time, so you can do a direct point equality test, like this:
for (id anObject in myArray) {
if (anObject == [NSNull null]) {
NSLog(#"object is nil");
}
else {
NSLog(#"object is not nil: %#", anObject);
}
}
create mutable array then just use
NSString *myString = [NSString stringWithFormat:#"ur new string"];
[myArray addObject:myString];
you have to add object type to array when adding new abject in mutable array.
hope this will help
Following code works fine:
NSDictionary *item;
if([arrTesting count] >0 )
item = [arrTesting objectAtIndex:0];
// The text "article" is what I'm searching for to ensure it exists
if([item objectForKey:#"article"] != [NSNull null])
{
NSString *mid = [[item objectForKey:#"article"] objectForKey:#"id"];
(more code below...)
But if I replace "article" with "/code/location/article" the app crashes. What gives?? What am I missing??
If a NSDictionary does not contain a specific element, objectForKey: will return nil and not [NSNull null].
So, if the dictionary does not contain the object that you are looking for, you are essentially doing a comparison like nil != [NSNull null] which will always evaluate to true. This is probably not what you want.
(Checking for NSNull means that the entry is there, but has a null value. This is common for for example JSON responses. I am not sure if it is common in your scenario though.)
I'm looking to create a "crash-proof" NSDictionary as I'm using a JSON serializer that converts a server response into an NSDictionary. As as result, sometimes the key for the dictionary is not present. Currently, this will crash the application, however I'd rather an empty NSString was returned so I can display this in the interface.
A solution could be that I check for the key every time I access the dictionary, e.g.
if([returnedDictionary objectForKey:#"key"]){
// Display [returnedDictionary objectForKey:#"key"];
}else{
// Display #"";
}
However this soon results in bloated, hard-to-read code.
I had thought about creating a custom NSDictionary object, something like:
#interface NSSafeDictionary : NSDictionary .....
that overrides objectForKey with the above statement.
Is this a satisfactory approach?
Thanks
Are you always going to want to get strings out of your dictionary or will other objects be stored in it as well? If it's only strings, I think the easiest way around this is to construct a category on NSDictionary.
#interface NSDictionary ( EmptyStrings )
- (NSString *)stringForKey:(id)aKey;
#end
#implementation NSDictionary ( EmptyStrings )
- (NSString *)stringForKey:(id)aKey {
id object = [self objectForKey:aKey];
if (object == nil ) {
return #"";
}
if ([object isKindOfClass:[NSString class]) {
return object;
} else {
return nil;
}
}
#end
Given that it comes in over the network, I would think that you would want to sanitise the data more than just checking for empty values but if not, you don't really need to inherit from NSDictionary.
A simple utility method in your class would do the trick.
Or you could create a category on NSDictionary:
#interface NSDictionary (Safe)
-(NSString*)safeStringForKey:(NSString*)key;
#end
(I'm sure you can figure out the implementation.)
NSArray *array = [[NSArray alloc] initWithObjects:#"ΕΛΤΑ",
#"ΕΛΤΑ COURIER", #"ACS", #"ACS ΕΞΩΤΕΡΙΚΟ",
#"DHL", #"INTERATTICA", #"SPEEDEX",
#"UPS", #"ΓΕΝΙΚΗ ΤΑΧΥΔΡΟΜΙΚΗ", #"ΜΕΤΑΦΟΡΙΚΕΣ ΕΞΩΤΕΡΙΚΟΥ", nil];
This is working because it has nil at the end.
But I add objects like this: addObject:name etc...
So at the end I have to add nil I do this addObhect:nil but when I run the app it still crashes at cellForRowAtIndexPath:
how can I do this work?
Ok, I dont have to add nil
What is the reason that my app crashes then?
If you must add a nil object to a collection, use the NSNull class:
The NSNull class defines a singleton object used to represent null values in collection objects (which don’t allow nil values).
Assuming "array" is of type NSMutableArray:
....
[array addObject:[NSNumber numberWithInt:2];
[array addObject:#"string"];
[array addObject:[NSNull null]];
You don't need to call [addObject:nil]
The nil in initWithObjects: is only there to tell the method where the list ends, because of how C varargs work. When you add objects one-by-one with addObject: you don't need to add a nil.
You can't add nil when you're calling addObject.
If you really want a Null-ish item in your collection, NSNull is there for that.
You need to add NSNull class and the best way to do it is like this:
NSArray *array = #[ #"string", #42, [NSNull null] ];
I personally recommend to use a specific value like 0 instead of null or nil in your design of your code, but sometimes you need to add null.
There is a good explanation from this Apple reference.
nil is used to terminate the array
nil is not an object that you can add to an array: An array cannot contain nil. This is why addObject:nil crashes.
pass your object through this method when adding to array to avoid attempt to insert nil object from objects crashes.
-(id) returnNullIfNil:(id) obj {
return (obj == nil) ? ([NSNull null]) : (obj);
}
[NSNull null] returns an null object which represents nil.
You can't add an object to an NSArray because that class is immutable. You have to use NSMutableArray if you want to change the array after it is created.