How to store multi-type data in NSArray? - iphone

NSArray *pets = [NSArray arrayWithObjects:#"Cat", #"Dog", #"Rat", nil];
// how do I store int value 456 in this array after #"Rat" object, + pet is of type NSString so will not it generate error in while loop...??? so what data-type should i use for pet pointer that can represent all nextObject values/objects
NSEnumerator *enumerator = [pets objectEnumerator];
NSString *pet;
while (pet = [enumerator nextObject]) {
NSLog(#"Pet: %#", pet);
}

Since NSArray will only hold object you can not add an integer, you will need to wrap it in a NSNumber.
NSArray *pets = #[#"Cat", #"Dog", #"Rat", #456];
This will work with the loop you have in your example, but if you want to call any NSString method you will need to check the type:
for(id pet in pets) {
if(![pet isKindOfClass:[NSString class]) {
// It not a string, just continue to the next object.
continue;
}
}
Or a while loop:
id pet;
NSEnumerator *enumerator = [pets objectEnumerator];
while (pet = [enumerator nextObject]) {
if(![pet isKindOfClass:[NSString class]) {
// It not a string, just continue to the next object.
continue;
}
}

Use #456, it's the same as [NSNumber numberWithInt:456]
Use id. i.e.:
id pet;
while (pet = [enumerator nextObject]) {
NSLog(#"Pet: %#", pet); // maybe unsafe if only a subset of the objects in the array can execute the methods
// if you want to unwrap the pet to the concrete class, cast it with (NSString *)pet
// but make sure the object is of correct type
if ([pet isKindOfClass: [NSString class]]) {// use isMemberOfClass for exactly this class and isKindOfClasses for this class and subclasses of it
// here you are sure it is a NSString
}
}

Related

How to swap `NSMutableDictionary` key and values in place?

I have a NSMutableDictionary and I want to swap values & keys. i.e, after swapping values becomes keys and its corresponding keys with become values All keys and values are unique. Looking for an in place solution because size is very big . Also, the keys and values are NSString objects
NSMutableDictionary *d = [NSMutableDictionary dictionaryWithDictionary:#{
#"key1" : #"value1",
#"key2" : #"value2"}];
for (NSString *key in [d allKeys]) {
d[d[key]] = key;
[d removeObjectForKey:key];
}
NSLog(#"%#", d); // => { value1 : key1,
// value2 : key2 }
Assumptions
unique values (as they will become keys)
values conform to NSCopying (same as above)
no value is equal to any key (otherwise colliding names will be lost in the process)
Here is another way to invert dictionary. The simplest for me.
NSArray *keys = dictionary.allKeys;
NSArray *values = [dictionary objectsForKeys:keys notFoundMarker:[NSNull null]];
[dictionary removeAllObjects]; // In case of huge data sets release the contents.
NSDictionary *invertedDictionary = [NSDictionary dictionaryWithObjects:keys forKeys:values];
[dictionary setDictionary:invertedDictionary]; // In case you want to use the original dictionary.
EDIT: I had written a few lines of codes to get the OP started into the task of creating his own algorithm. The answer was not well received so I have crafted a full implementation of an algorithm that does what he asks, and goes one step further.
Advantages:
Makes no assumptions regarding the contents of the dictionary, for example, the values need not conform to the 'NSCopying' protocol
Transverses the whole hierarchy of a collection, swapping all the keys
It's fast since it uses recursion and fast enumeration
Does not alter the contents of the original dictionary, it creates a brand new one
Code has been implemented through categories to both collections:
#interface NSDictionary (Swapping)
- (NSDictionary *)dictionaryBySwappingKeyWithValue;
#end
#interface NSDictionary (Swapping)
- (NSDictionary *)dictionaryBySwappingKeyWithValue
{
NSMutableDictionary *mutableDictionary = [NSMutableDictionary dictionaryWithCapacity:self.count];
[self enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
id newKey = nil;
if ([value isKindOfClass:[NSDictionary class]]) {
newKey = [value dictionaryBySwappingKeyWithValue];
} else if ([value isKindOfClass:[NSArray class]]) {
newKey = [value arrayBySwappingKeyWithValue];
} else {
newKey = value;
}
if (![newKey conformsToProtocol:#protocol(NSCopying)]) {
newKey = [NSValue valueWithNonretainedObject:newKey];
}
mutableDictionary[newKey] = key;
}];
return [NSDictionary dictionaryWithDictionary:mutableDictionary];
}
#end
and...
#interface NSArray (Swapping)
- (NSArray *)arrayBySwappingKeyWithValue;
#end
#implementation NSArray (Swapping)
- (NSArray *)arrayBySwappingKeyWithValue
{
NSMutableArray *mutableArray = [NSMutableArray arrayWithCapacity:self.count];
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[NSDictionary class]]) {
NSDictionary *newDict = [obj dictionaryBySwappingKeyWithValue];
mutableArray[idx] = newDict;
} else if ([obj isKindOfClass:[NSArray class]]) {
NSArray *newArray = [obj arrayBySwappingKeyWithValue];
mutableArray[idx] = newArray;
} else {
mutableArray[idx] = obj;
}
}];
return [NSArray arrayWithArray:mutableArray];
}
#end
As an example, assume you have a dictionary with the following structure:
UIView *view = [[UIView alloc] init];
NSDictionary *dict = #{#"1" : #"a",
#"2" : #[ #{ #"5" : #"b" } ],
#"3" : #{#"6" : #"c"},
#"7" : view};
NSDictionary *newDict = [dict dictionaryBySwappingKeyWithValue];
Printing the newDict object in the console will give you this output:
(lldb) po mutableDictionary
{
a = 1;
({b = 5;}) = 2;
{c = 6;} = 3;
"<30b50617>" = 7;
}
As you can see, not only have the keys and values been swapped at the first level of the hierarchy, but deep inside each collection.
"<30b50617>" represents the UIView object wrapped inside a NSValue. Since UIView does not comply to the NSCopying protocol, it needs to be handled this way if you want it to be a key in your collection.
Note: Code was done in a couple of minutes. Let me know if I missed something.
for (NSString *key in [myDictionary allKeys]) {
NSString *value = [responseDataDic objectForKey:key];
[myDictionary removeObjectForKey:key];
[myDictionary addObject:key forKey:value];
}
Assumption:
No key = value;
Complexity:
No extra space required. Will loop through once and replace all key value pairs.
NSArray* allKeys = [theDict allKeys];
NSArray* allValues = [theDict allValues];
NSMutableDictionary* newDict = [NSMutableDictionary dictionaryWithObjects:allKeys forKeys:allValues];

Memory Leak in NSObject+JSONSerializableSupport

while removing the runtime memory leaks in my iPad application , I came across this strange memory leak in NSObject+JSONSerializableSupport class in the following method
+ (id) deserializeJSON:(id)jsonObject {
id result = nil;
if ([jsonObject isKindOfClass:[NSArray class]]) {
//JSON array
result = [NSMutableArray array];
for (id childObject in jsonObject) {
[result addObject:[self deserializeJSON:childObject]];
}
}
else if ([jsonObject isKindOfClass:[NSDictionary class]]) {
//JSON object
//this assumes we are dealing with JSON in the form rails provides:
// {className : { property1 : value, property2 : {class2Name : {property 3 : value }}}}
NSString *objectName = [[(NSDictionary *)jsonObject allKeys] objectAtIndex:0];
Class objectClass = NSClassFromString([objectName toClassName]);
if (objectClass != nil) {
//classname matches, instantiate a new instance of the class and set it as the current parent object
result = [[[objectClass alloc] init] autorelease];
}
NSDictionary *properties = (NSDictionary *)[[(NSDictionary *)jsonObject allValues] objectAtIndex:0];
NSDictionary *objectPropertyNames = [objectClass propertyNamesAndTypes];
for (NSString *property in [properties allKeys]) {
NSString *propertyCamalized = [[self convertProperty:property andClassName:objectName] camelize];
if ([[objectPropertyNames allKeys]containsObject:propertyCamalized]) {
Class propertyClass = [self propertyClass:[objectPropertyNames objectForKey:propertyCamalized]];
[result setValue:[self deserializeJSON:[propertyClass deserialize:[properties objectForKey:property]]] forKey:propertyCamalized];
}
}
}
else {
//JSON value
result = jsonObject;
}
return result;
}
I am getting the memory leak on this line
[result setValue:[self deserializeJSON:[propertyClass deserialize:[properties objectForKey:property]]] forKey:propertyCamalized];
Please suggest a solution or tell me where i am going wrong.

Not Null validation for JSON Response iPhone App

Currently I am using the following method to validate the data for not being Null.
if ([[response objectForKey:#"field"] class] != [NSNull class])
NSString *temp = [response objectForKey:#"field"];
else
NSString *temp = #"";
Problem comes when the response Dictionary contains hundreds of attributes (and respective values). I need to add this kind of condition to each and every element for the dictionary.
Any other way around to accomplish?
Any Suggestion for making any change to the web service (except not inserting the null value to database)?
Any Idea, Anyone ??
What I've done is put a category on NSDictionary
#interface NSDictionary (CategoryName)
/**
* Returns the object for the given key, if it is in the dictionary, else nil.
* This is useful when using SBJSON, as that will return [NSNull null] if the value was 'null' in the parsed JSON.
* #param The key to use
* #return The object or, if the object was not set in the dictionary or was NSNull, nil
*/
- (id)objectOrNilForKey:(id)aKey;
#end
#implementation NSDictionary (CategoryName)
- (id)objectOrNilForKey:(id)aKey {
id object = [self objectForKey:aKey];
return [object isEqual:[NSNull null]] ? nil : object;
}
#end
Then you can just use
[response objectOrNilForKey:#"field"];
You can modify this to return a blank string if you'd like.
First a minor point: your test is not idiomatic, you should use
if (![[response objectForKey:#"field"] isEqual: [NSNull null]])
If you want all keys in your dictionary that have a value of [NSNull null] to be reset to the empty string, the easiest way to fix it is
for (id key in [response allKeysForObject: [NSNull null]])
{
[response setObject: #"" forKey: key];
}
The above assumes response is a mutable dictionary.
However, I think you really need to review your design. You shouldn't be allowing [NSNull null] values at all if they are not allowed in the database.
It's not quite clear for me what you need but:
If you need to check whether the value for key is not NULL you can do this:
for(NSString* key in dict) {
if( ![dict valueForKey: key] ) {
[dict setValue: #"" forKey: key];
}
}
If you have some set of required keys, you can create static array and then do this:
static NSArray* req_keys = [[NSArray alloc] initWithObjects: #"k1", #"k2", #"k3", #"k4", nil];
Then in the method where you check your data:
NSMutableSet* s = [NSMutableSet setWithArray: req_keys];
NSSet* s2 = [NSSet setWithArray: [d allKeys]];
[s minusSet: s2];
if( s.count ) {
NSString* err_str = #"Error. These fields are empty: ";
for(NSString* field in s) {
err_str = [err_str stringByAppendingFormat: #"%# ", field];
}
NSLog(#"%#", err_str);
}
static inline NSDictionary* DictionaryRemovingNulls(NSDictionary *aDictionary) {
NSMutableDictionary *returnValue = [[NSMutableDictionary alloc] initWithDictionary:aDictionary];
for (id key in [aDictionary allKeysForObject: [NSNull null]]) {
[returnValue setObject: #"" forKey: key];
}
return returnValue;
}
response = DictionaryRemovingNulls(response);

NSArray of many NSDictionary. What is the best way to find a NSDictionary with necessary value for given key?

Now I'm trying the following and it works.
- (void)findDictionaryWithValueForKey:(NSString *)name {
for (NSDictionary * set in myArray) {
if ([[set objectForKey:#"title"] isEqualToString:name])
\\do something
}
}
EDIT:
I've added one extra argument to the post of bshirley. Now it looks more flexible.
- (NSDictionary *)findDictionaryWithValue:(NSString*)name forKey:(NSString *)key {
__block BOOL found = NO;
__block NSDictionary *dict = nil;
[self.cardSetsArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
dict = (NSDictionary *)obj;
NSString *title = [dict valueForKey:key];
if ([title isEqualToString:name]) {
found = YES;
*stop = YES;
}
}];
if (found) {
return dict;
} else {
return nil;
}
}
Here's one possible implementation using newer API. (I also modified the method to actually return the value). Provided mostly to demonstrate that API. The assumption is that the title is unique to one dictionary within your array.
- (NSDictionary *)findDictionaryWithValueForKey:(NSString *)name {
// ivar: NSArray *myArray;
__block BOOL found = NO;
__block NSDictionary *dict = nil;
[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
dict = (NSDictionary *)obj;
NSString *title = [dict valueForKey:#"title"];
if ([title isEqualToString:name]) {
found = YES;
*stop = YES;
}
}];
if (found) {
return dict;
} else {
return nil;
}
}
Use filteredArrayUsingPredicate: method of the array to get all the dictionaries that satisfy your requirement.
NSPredicate * predicate = [NSPredicate predicateWithFormat:#" title MATCHES[cd] %#", name];
NSArray * matches = [myArray filteredArrayUsingPredicate:predicate];
Now matches is the array of dictionaries that have the title key equal to name.
- (void)findDictionaryWithValueForKey:(NSString)name {
for (NSDictionary * set in myArray) {
NSString *s=[NSString stringWithFormat:#"%#",[set objectForKey:#"title"]];
if ([s isEqualToString:name])
\\do something
}
OR
if (s == name])
\\do something
}
}
I will also suggest this way,it would be better if you use a break statement,
- (void)findDictionaryWithValueForKey:(NSString)name {
for (NSDictionary * set in myArray) {
if ([[set objectForKey:#"title"] isEqualToString:name])
\\do something
break;
}
}
As per the NSArray documentation,
valueForKey:
Returns an array containing the results of invoking valueForKey: using key on each of the array's objects.
- (id)valueForKey:(NSString *)key
Parameters
key
The key to retrieve.
Return Value
The value of the retrieved key.
Discussion
The returned array contains NSNull elements for each object that returns nil.
Availability
* Available in Mac OS X v10.3 and later.
EDIT:
try this,
[myArray valueForKey:#"name"];
//this will return array of values, but this actually differ from what to want

How to use NSEnumerator with NSMutableDictionary?

How do I use NSEnumerator with NSMutableDictionary, to print out all the keys and values?
Thanks!
Unless you need to use NSEnumerator, you can use fast enumeration (which is faster) and concise.
for(NSString *aKey in myDictionary) {
NSLog(#"%#", aKey);
NSLog(#"%#", [[myDictionary valueForKey:aKey] string]); //made up method
}
Also you can use an Enumerator with fast enumeration:
NSEnumerator *enumerator = [myDictionary keyEnumerator];
for(NSString *aKey in enumerator) {
NSLog(#"%#", aKey);
NSLog(#"%#", [[myDictionary valueForKey:aKey] string]); //made up method
}
This is useful for things like doing the reverse enumerator in an array.
From the NSDictionary class reference:
You can enumerate the contents of a dictionary by key or by value using the NSEnumerator object returned by keyEnumerator and objectEnumerator respectively.
In other words:
NSEnumerator *enumerator = [myMutableDict keyEnumerator];
id aKey = nil;
while ( (aKey = [enumerator nextObject]) != nil) {
id value = [myMutableDict objectForKey:anObject];
NSLog(#"%#: %#", aKey, value);
}
Here is the version without object search. Notice that objectForKey calling not exist.
They use keyEnumerator and objectEnumerator both.
id aKey = nil;
NSEnumerator *keyEnumerator = [paramaters keyEnumerator];
NSEnumerator *objectEnumerator = [paramaters objectEnumerator];
while ( (aKey = [keyEnumerator nextObject]) != nil) {
id value = [objectEnumerator nextObject];
NSLog(#"%#: %#", aKey, value);
}