TijuanaKez
TijuanaKez

Reputation: 1472

How to remove <null>'s from NSDictionary?

I'm trying to use the writeToFile method of an NSArray of NSDictionaries which doesn't allow nulls. I figured just loop through and find them and replace with empty NSString's but I can't get it to work. This is what I'm trying.

EDIT: The NSArrays and NSDictionaries are created by NSJSONSerialization.

for (NSMutableDictionary *mainDict in mainArray)
{
    for(NSMutableDictionary *subDict in mainDict)
    {
        for(NSString *key in subDict)
        {                
            id value = [subDict objectForKey:key];

            if (value == [NSNull null])
            {
                [subDict setObject:@"" forKey:key];
            }
        }
    }
}

But this throws an exception. *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object'

But as you can see all NSDictionaries are mutable.

What's up? Is there a better way to do this?

EDIT: Moved new version to a separate answer.

Upvotes: 0

Views: 1154

Answers (3)

Motti Shneor
Motti Shneor

Reputation: 2194

  1. NSDictionary does not allows any null values. Only [NSNull null] object instances.
  2. You cannot modify entries of NSDictionary. Only NSMutableDictionary entries. It seems your mainArray contains Immutable NSDictionary instances.

A graceful way to do what you want (BTW, are your sure you want empty strings in your file instead of removing keys whose values are [NSNull null] ?) is by creating a filtered and adjusted copy of your array and dictionaries -- and write that to file.

NSMutableArray *tempArray = [[NSMutableArray alloc] initWithCapacity:mainArray.count]
for (NSDictionary *mainDict in mainArray) { // scan the dictionaries in your mainArray
    NSMutableDictionary *dict = [mainDict mutableCopy];
    // replace NSNull objects with empty NSString objects
    [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull value, BOOL * _Nonnull stop) {
        if (value == [NSNull null])
            [dict setObject:@"" forKey:key];
    }];
    [tempArray addObject:dict];
}
[tempArray writeToFile:... ];

Upvotes: 0

TijuanaKez
TijuanaKez

Reputation: 1472

Or rebuilding the whole tree works.

NSMutableArray *newMainArray = [[NSMutableArray alloc] init];
for (NSArray *subArray in self.mainArray)
{
    NSMutableArray *newSubArray = [[NSMutableArray alloc] init];
    for(NSDictionary *dict in subArray)
    {
        NSMutableDictionary *newDict = [[NSMutableDictionary alloc] init];
        for(NSString *key in dict)
        {              
            id value = [dict objectForKey:key];
            if (value == [NSNull null])
            {
                [newDict setObject:@"" forKey:key];
            } else {
                [newDict setObject:value forKey:key];
            }
        }
        [newSubArray addObject:newdict];
        [newDict release];
    }
    [newMainArray addObject:newSubArray];
    [newSubArray release];
}
[self.mainArray release];     <----  ????
self.mainArray = newMainArray;

Upvotes: 0

zoul
zoul

Reputation: 104095

How do you know that the dictionaries are mutable? It looks like they’re not. Typing them as NSMutableDictionary during the enumeration doesn’t make them mutable. You could walk over the dictionary and move the keys into a new, mutable one, filtering the nulls.

Also, if your dictionary comes from NSJSONSerialization, take a look at NSJSONReadingOptions, there’s an option to use mutable containers for the deserialized structure. That’s not always a good solution, but it’s worth checking out.

As for mutating a dictionary while enumerating it: you can’t change the keys, since that could easily throw off the enumeration code. But I don’t see why you couldn’t change the key values, which is all you’re after. This code works:

NSMutableDictionary *dict = [@{@"foo" : @"bar"} mutableCopy];
for (NSString *key in dict) {
    dict[key] = @"baz";
}

Upvotes: 5

Related Questions