Replacing Object in Dictionary in an Array - ios

I have an array structure as follows:
NSMutableArray topArray{
NSMutableArray middleArray{
NSMutableArray lowerArray{
NSMutableDictionary dict{
}
}
}
}
The array structure is filled with some data that I retrieve from the web is JSON format.
I am trying to edit one of the objects in the NSMutableDictionary as follow:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSMutableArray *lowerArray = [[self middleArray] objectAtIndex:[indexPath section]];
NSMutableDictionary *dict = [lowerArray objectAtIndex:[indexPath row]];
[dict setObject:[NSNumber numberWithInt:1] forKey:#"key"];
[[self tableView] reloadData];
}
The data within each of the arrays is correct (I have checked with print statements), however, when I try to change the object in the dict I get the following error:
reason:
'-[__NSCFDictionary setObject:forKey:]: mutating method sent to
immutable object'
I need the object in the dictionary to be changed within the array structure.
Could this be an issue with the JSON data since when topArray is first initialised with the JSON data the middle and lower arrays are in the form of just NSArray's?
Sorry if this is confusing, I will try to clarify more if you have any questions.
Thanks in advance for your help.

If you're using NSJSONSerialization, you can pass NSJSONReadingMutableContainers to the options parameter of +JSONObjectWithData:options:error:, and all of the parsed dictionaries and arrays will be mutable.
NSMutableArray *topArray = [NSJSONSerialization JSONObjectWithData:webServiceData
options:NSJSONReadingMutableContainers
error:nil];

Just use NSMutableDictionary class instead of just NSDictionary for the moduleDict variable. It is easily done when parsing JSON objects. If no - create one like this:
NSMutableDictionary *newDict = [NSMutableDictionary dictionaryWithDictionary: moduleDict];

I doubt that you are really dealing with NSMutableDictionary on the lowest level. Do you have the output from these print statements for us?
Usually the structures generated by JSON deserialisation are of immutable type. And that is exactly what your error message is telling.

Related

Convert NSmangedObject to JsonModel

In my application I am using a JsonModel for parsing JSON response from server and while storing it in Core Data I am using NSManagedObject and NSManagedContext which is provided by Apple itself. Now whenever I fetch I want to convert the NSManagedObject to JsonModel. Now the problem is I have to use two separate class to manage jsonModel and NSManagedObject.
You can get help from this link.
Get Core Data objects to JSON
Or in easy way from andrew-madsen's Answer
NSManagedObject *managedObject = ...;
NSArray *keys = [NSArray arrayWithObjects:#"key1", #"key2", ..., nil]; // These are the keys for the properties of your managed object that you want in the JSON
NSString *json = [[managedObject dictionaryWithValuesForKeys:keys] JSONRepresentation];
For more detail try this link
nsmanagedobject-to-json
Hope it helps you. first try to convert your NSMangedObject to NSDictionary.
NSArray *keys = [[[yourObject entity] attributesByName] allKeys];
NSDictionary *dict = [myObject dictionaryWithValuesForKeys:keys];
the you have to use dict as a JSON or convert them into JSON string if needed.

Add object to array within NSMutableDictionary loaded from NSUserDefaults

I have an array inside a NSMutableDictionary and i want to add objects to it. With my current approach I get an error saying that the array is immutable.
I think the problem lies when I´m saving the dictionary to NSUserDefaults. I´m retrieving the is it a NSDictionary but I am at the same time creating a new NSMutableDictionary with the contents.
However, the array seems to be immutable. How do I replace an array inside of a dictionary?
My dictionary looks like this:
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:[NSNumber numberWithInt:0], nil];
NSDictionary *dict = #{
#"firstKey": #{
#"theArray":array,
}
};
NSMutableDictionary *mutDict = [[NSMutableDictionary alloc] initWithDictionary:dict];
I am trying to add objects like this:
[[[mutDict objectForKey:#"firstKey"] objectForKey:#"theArray"] addObject:[NSNumber numberWithInt:5]];
I am able to add objects to the array inside mutDict before its saved to NSUserDefaults
The error message I get when I try to add to the array inside the dictionary after loading it from NSUserDefaults:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[__NSCFArray insertObject:atIndex:]: mutating method sent to immutable object'
Here's what the documentation for dictionaryForKey: says on NSUserDefaults:
Special Considerations
The returned dictionary and its contents are immutable, even if the values you >originally set were mutable.
So when you retrieve your dictionary from NSUserDefaults the dictionary itself and all of the collections inside it are immutable. You can make the top level dictionary mutable (which I assume you are doing), but that won't propagate down into the now immutable NSArrays which are values in the dictionary.
The only way to get around this is to go through the dictionary that's returned and replace the immutable NSArrays with their mutable counterparts. It might look something like this.
- (NSMutableDictionary *)deepMutableCopyOfDictionary:(NSDictionary *)dictionary
{
NSMutableDictionary *mutableDictionary = [dictionary mutableCopy];
for (id key in [mutableDictionary allKeys]) {
id value = mutableDictionary[key];
if ([value isKindOfClass:[NSDictionary class]]) {
// If the value is a dictionary make it mutable and call recursively
mutableDictionary[key] = [self deepMutableCopyOfDictionary:dictionary[key]];
}
else if ([value isKindOfClass:[NSArray class]]) {
// If the value is an array, make it mutable
mutableDictionary[key] = [(NSArray *)value mutableCopy];
}
}
return mutableDictionary;
}
To be honest though it sounds like you're using NSUserDefaults for something a lot more complex then it is intended for. If you want to persist complex data structures then you should look into something like Core Data, or if that looks to be a bit overkill take a look at NSKeyedArchiver.
You can add object directly to the array:
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:[NSNumber numberWithInt:0], nil];
NSDictionary *dict = #{
#"firstKey": #{
#"theArray":array,
}
};
NSMutableDictionary *mutDict = [[NSMutableDictionary alloc] initWithDictionary:dict];
//Since Objective-C objects are always passed by reference (using pointers) you can add object to the array
[array addObject:[NSNumber numberWithInt:55]];
Swift example of adding object to array which is part of a dictionary.
let arr = [0] // note that initial array may be immutable
var dict = ["fK": ["a":arr]] // even if "arr" will be mutable, but "dict" immutable
dict["fK"]!["a"]!.append(3) // this will not work. "dict" must be mutable
println(dict) //[fK: [a: [0, 3]]]
Another approach
var arr = [0] // initial array must be mutable
var dict = ["fK": ["a":arr]] // in both cases dictionary must be mutable
arr.append(3)
let newArr = arr
dict["fK"]!["a"]! = newArr // because we change it's content
println(dict) //[fK: [a: [0, 3]]]

Use NSDictionary as other NSDictionary's key

i'm trying to achieve the following structure:
NSMutableDictionary *dict = [#{} mutableCopy];
NSDictionary *key1 = #{#"id_format": #(1), #"date": #"2014-08-01"};
NSDictionary *key2 = #{#"id_format": #(2), #"date": #"2014-08-02"};
// This runs perfect and can be checked in llvm debugger
// data1 & data2 are NSArray that contain several NSDictionary
[dict setObject:data1 forKey:key1];
[dict setObject:data2 forKey:key2];
// Later, if i try to access dict using another key, returns empty NSArray
NSDictionary *testKey = #{#"id_format": #(1), #"date": #"2014-08-01"}; // Note it's equal to "key1"
for(NSDictionary *dictData in dict[testKey]){
// dictData is empty NSArray
}
// OR
for(NSDictionary *dictData in [dict objectForKey:testKey]){
// dictData is empty NSArray
}
So the question is if is there possible to use NSDictionary as key, or not.
An object can be used as a key if it conforms to NSCopying, and should implement hash and isEqual: to compare by value rather than by identity.
Dictionaries follow the array convention of returning [self count] for hash. So it's a pretty bad hash but it's technically valid. It means your outer dictionary will end up doing what is effectively a linear search but it'll work.
Dictionaries implement and correctly respond to isEqual:. They also implement NSCopying.
Therefore you can use a dictionary as a dictionary key.

Converting a NSDictionary into a NSMutableDictionary in a for loop

I have the following code:
for (NSMutableDictionary *aDict in array)
{
// do stuff
[aDict setObject:myTitle forKey:#"title"];
}
My question is, if the array is filled with NSDictionary objects, will this for loop code as written automatically convert them into NSMutableDictionary objects?
Or do I need to do something more specific here to ensure that I don't get an unrecognized selector sent to instance error on setObject:forKey: in the loop?
Currently that will give you the error you mentioned. Whilst the loop is setup with mutable dictionaries, the underlying object is still immutable. You'd need to create a new dictionary out of it. Try this
NSMutableArray *newArray = [NSMutableArray array];
for (NSDictionary *aDict in array)
{
NSMutableDictionary *mutable = [aDict mutableCopy];
// do stuff
[mutable setObject:myTitle forKey:#"title"];
[newArray addObject:mutable];
}
No it will not automatically convert them. You have to do that yourself. You'll definitely get the unrecognized selector sent to instance exception.

Set value for key of a single NSMutableDictionary from an array of NSMutableArray

While trying to set a single key/value pair in NSMutableDictionary of NSMutableArray like:
[[self.items objectAtIndex:i] setValue:#"value" forKey:#"key"];
self.items is NSMutableArray and it have a list of NSMutableDictionaries
Instead of setting to that single object, it set it to all dictionaries in the list.
I have used this method before. But I don't know what is happening in this case.
I know NSArray have setValue:#"value" forKey:#"key method, but in this case I am using NSMutableArray
Here is a bit more block of code to help clarify my situation:
-(void)setItem:(id)sender
{
for (CellView *cell in self.CollectionView.visibleCells)
{
NSIndexPath *indexPath = [self.CollectionView indexPathForCell:cell];
int i = (indexPath.section * (mainItems.count)/3+ indexPath.row);
if (((UIButton *)sender).tag == i)
{
[[self.items objectAtIndex:i] setValue:#"value" forKey:#"key"];
}
}
}
Call setObject:forKey:, not setValue:forKey:. There is a difference.
Note that NSMutableArray extends NSArray so NSMutableArray has all of the methods of NSArray.
I also recommend you split your line up as well as use modern syntax:
NSMutableDictionary *dict = self.items[i];
dict[#"key"] = #"value";
NSMutableArray is a subclass of NSArray so all the NSArray methods are still there in NSMutatbleArray. You could try pulling it out and putting it back in to figure things out then reassemble your code after...
NSMutableDictionary *d = [self.items objectAtIndex:i];
[d setValue:#"value" forKey:#"key"];
[self.items setObject: d atIndexedSubscript: i];
This is a little more explicit which will allow you to debug it easier (not getting unexpected nils back, etc.).
Ok I got the issue.
I am working on a large pre-existing code. And I come to notice that The MutableDictionary was defined globally and the same object was being added to the MutableArray. So basically all the pointers in the MUtableArray where pointing to a single object.

Resources