Change value in a nested NSMutableDictionary - ios

I have a NSMutableDictionary to which I want to change the value of an element.
//My dictionary:
{
objectId = 8ED998yWd1;
cardInfo = {
state = published; //THIS!
price = 40;
color = red;
}
};
I have tried several ways, but the value does not change, like this:
[dictionary setObject:#"reserved" forKey:#"state"]; //nope
or this:
[dictionary setValue:#"reserved" forKeyPath:#"cardInfo.state"]; //nope
or that:
[[dictionary objectForKey:#"cardInfo"] setObject:#"reserved" forKey:#"state"]; //no
and this:
[dictionary setObject:#"reserved" forKey:[[dictionary objectForKey:#"cardInfo"] objectForKey:#"state"]];
How can I change the object "state" from "published" to "reserved"?
Thank you!

Assuming that both dictionary and cardInfo are NSDictionary instances:
You could get a mutable copy of the nested dictionary, modify the appropriate value, and write the modified dictionary back into the "top level" dictionary, like so:
NSMutableDictionary *mutableDict = [dictionary mutableCopy];
NSMutableDictionary *innerDict = [dictionary[#"cardInfo"] mutableCopy];
innerDict[#"state"] = #"reserved";
mutableDict[#"cardInfo"] = innerDict;
dictionary = [mutableDict copy];
I guess you could squeeze that in one line, but it would be one ugly line.
Edit:
If both the outer and inner dictionary were already mutable that would simplify things a bit, of course:
NSMutableDictionary *innerDict = dictionary[#"cardInfo"];
innerDict[#"state"] = #"reserved";
dictionary[#"cardInfo"] = innerDict;

Related

Sort an NsmutableDictionary Alphabetically [duplicate]

This question already has answers here:
sort NSDictionary values by key alphabetical order
(4 answers)
Closed 6 years ago.
I have a dictionary in which, for a single key(for example key "0") there are a key value pair data.The keys are like name, id,p_id. I want to sort the NSMutableDictionary for the values related to the Key "name". The data in the dictionary is as follows,
0 = {
id = 12;
name = "Accounts ";
"p_id" = 13222071;
};
1 = {
id = 13;
name = "consultant";
"p_id" = 15121211;
};
2 = {
id = 11;
name = "Tania";
"p_id" = 10215921;
};
}
Any help is appreciated!
Please try out the below code:
[yourMutableArray sortUsingComparator: (NSComparator)^(NSDictionary *a, NSDictionary *b) {
NSString *key1 = [a objectForKey: #"name"];
NSString *key2 = [b objectForKey: #"name"];
return [key1 compare: key2];
}];
NSLog(#"Sorted Array By name key : %#", yourMutableArray);
Hope this helps!
NSArray *sortedKeys = [dict.allKeys sortedArrayUsingComparator:^NSComparisonResult(NSDictionary *d1, NSDictionary *d2) {
return [d1[#"name"] compare:d2[#"name"]];
}];
NSArray *objects = [dict objectsForKeys:sortedKeys notFoundMarker:[NSNull null]];
Dictionaries are not sorted, and doesn't resemble any order. What you should do is to getAll the keys first. Then apply a sort method on the keys, then request the objects according to the ordered keys.
E.g:
NSArray *keys = [dictionary allKeys];
NSArray *sortedKeys = <sort the keys according to your preferred method>
Now you can iterate the Dictionary from the order of the array sortedKeys.
While it has been made abundantly clear that Dictionaries can't be sorted and rightfully so, that does not mean the ends you are aiming for can't be achieved. This code will do that for you:
NSArray *arrayOfDicts = dic.allValues; //Now we got all the values. Each value itself is a dictionary so what we get here is an array of dictionaries
NSSortDescriptor *nameDescriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES]; //Create sort descriptor for key name
NSArray *sortingDesc = [NSArray arrayWithObject:nameDescriptor];
NSArray *sortedArray = [arrayOfDicts sortedArrayUsingDescriptors:sortingDesc]; //Get sorted array based on name
NSMutableDictionary *kindaSortedDict = [[NSMutableDictionary alloc] init];
int keyForDict=0;
for(NSDictionary *valDict in sortedArray)
{
[kindaSortedDict setObject:valDict forKey:[NSString stringWithFormat:#"%i",keyForDict]]; //Set values to our new dic which will be kind of sorted as the keys will be assigned to right objects
keyForDict++;
}
//Now you can simply get sorted array of keys from kindaSortedDic and results for them will always be sorted alphabetically. Alternatively you can just skip all that bother and directly use sortedArray
I have added comments in code to help you understand that.
For accessing sorted values I'd do this:
NSArray *sortedKeys = [kindaSortedDict.allKeys sortedArrayUsingDescriptors:
#[[NSSortDescriptor sortDescriptorWithKey:#"intValue"
ascending:YES]]];
for(NSString *key in sortedKeys)
{
NSDictionary *valDict = [kindaSortedDict objectForKey: key];
NSLog(#"Dict is: %# for key: %#",valDict,key);
}

how to array value convert in dictionary objective c

How to convert an array into a dictionary in Objective-C language?
This is the array:
[{"1":"2"}, {"2":"3"}]
But I want :
{"1":"2", "2":"3"}
Please help me, I am a new iPhone developer.
I am assuming that you want to combine an array of dictionaries into a single dictionary.
NSArray *array = #[#{#"1":#"2"},#{#"2":#"3"}];
NSMutableDictionary *result = [[NSMutableDictionary alloc]init];
for (NSDictionary *dict in array)
{
[result addEntriesFromDictionary:dict];
}
/*
result = {
1 = 2;
2 = 3;
}
*/
Where did your data come from? The easiest way to "convert" the above is to simply create a single dictionary in the first place.
Otherwise:
NSMutableDictionary* newDict = [NSMutableDictionary dictionary];
for (NSDictionary* oldDict in sourceArray) {
[newDict addEntriesFromDictionary:oldDict];
}

Multiple dictionaries within an array and Checking for duplicate keys - Objective C

I have an array which contains multiple Dictionaries each one with 3 keys (#"date", #"username", #"text").
What I want to check for, is whether the same user (#"username") exists in more than one dictionary in that Array. And, if she does, combine the text for those "duplicates" into one dictionary.
I have considered this answer to check for duplicates and this one
but I cannot figure out how to combine these two.
Jumping in here because although I think you should work on the code yourself first, I think Miro's answer is more complicated than the issue requires and though I like the idea of using predicates in Greg's answer, here's a 3rd solution that (1) wouldn't require you to change your data structure and (2) references the necessary loops...
The way I'd do it: Create an NSMutableArray then start adding the usernames in order. If the NSMutableArray already contains the username though, don't add another instance of the username, but instead merge the dictionary info.
ex.
// Note: I'm calling your array of user dictionaries userArray.
// Create a username array to store the usernames and check for duplicates
NSMutableArray *usernames = [[NSMutableArray alloc] init];
// Create a new userArray to store the updated dictionary info, merged
// entries et. al.
NSMutableArray *newUserArray = [[NSMutableArray alloc] init];
// Go through the array of user dictionaries
for (NSDictionary *userDict in userArray) {
// If the usernames array doesn't already contain the username,
// add it to both the usernames array and the newUserArray as is
if (![usernames containsObject:[userDict objectForKey:#"username"]]) {
[usernames addObject:[userDict objectForKey:#"username"]];
[newUserArray addObject:userDict];
}
// Otherwise, merge the userArray entries
else {
// Get a mutable copy of the dictionary entry at the first instance
// with this username
int indexOfFirstInstance = [usernames indexOfObject:[userDict objectForKey:#"username"]];
NSMutableDictionary *entry = [[newUserArray objectAtIndex:indexOfFirstInstance] mutableCopy];
// Then combine the "text" or whatever other values you wanted to combine
// by replacing the "text" value with the combined text.
// (I've done so with a comma, but you could also store the value in an array)
[entry setValue:[[entry objectForKey:#"text"] stringByAppendingString:[NSString stringWithFormat:#", %#", [userDict objectForKey:#"text"]]] forKey:#"text"];
// Then replace this newly merged dictionary with the one at the
// first instance
[newUserArray replaceObjectAtIndex:indexOfFirstInstance withObject:entry];
}
}
Maybe something like this [untested] example? Loop through, maintain a hash of existing items, and if a duplicate is found then combine with existing and remove.
NSMutableArray main; // this should exist, with content
NSMutableDictionary *hash = [[NSMutableDictionary alloc] init];
// loop through, backwards, as we're attempting to modify array in place (risky)
for(int i = [main count] - 1; i >= 0; i--){
// check for existing
if(hash[main[i][#"username"]] != nil){
int existingIdx = [hash[main[i][#"username"]] integerValue]; // get existing location
main[existingIdx][#"text"] = [main[existingIdx][#"text"] stringByAppendingString:main[i][#"text"]]; // "combine text" .. or however you'd like to
[main removeObjectAtIndex:i]; // remove duplicate
} else {
[hash setValue:[[NSNumber alloc] initWithInt:i] forKey:main[i][#"username"]]; // mark existance, with location
}
}
If you use NSMutableDictionary, NSMutableArray and NSMutableString you can do it with predicate like that:
NSMutableDictionary *d1 = [#{#"username": #"Greg", #"text" : [#"text 1" mutableCopy]} mutableCopy];
NSMutableDictionary *d2 = [#{#"username": #"Greg", #"text" : [#"text 2" mutableCopy]} mutableCopy];
NSMutableDictionary *d3 = [#{#"username": #"John", #"text" : [#"text 3" mutableCopy]} mutableCopy];
NSMutableArray *array = [#[d1, d2, d3] mutableCopy];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"username = %#", #"Greg"];
NSArray *filterArray = [array filteredArrayUsingPredicate:predicate];
NSMutableDictionary * firstDict = filterArray[0];
for (NSDictionary *d in filterArray)
{
if (firstDict != d)
{
[firstDict[#"text"] appendString:d[#"text"]];
[array removeObject:d];
}
}

Merging dictionaries - Incompatible type error

I've been trying to merge two NSDictionaries for a couple hours now. Searched and found that I can use [NSMutableDictionary addEntriesFromDictionary:].
NSDictionary *areaAttributes = [[area entity] attributesByName];
NSDictionary *gpsAttributes = [[gps entity] attributesByName];
NSMutableDictionary *areaAttributesM = [areaAttributes mutableCopy];
NSMutableDictionary *gpsAttributesM = [gpsAttributes mutableCopy];
NSMutableDictionary *combinedAttributes = [areaAttributesM addEntriesFromDictionary:gpsAttributesM];
But I get the error:
Initializing 'NSMutableDictionary *_strong' with an expression of incompatible type 'void'
So this is saying that [areaAttributesM addEntriesFromDictionary:gpsAttributesM] returns void? Is my understanding correct? And why is it returning void?
Yes, you are correct. From the docs:
- (void)addEntriesFromDictionary:(NSDictionary *)otherDictionary
As to why, that's simple: Functions that mutate an object in place in Cocoa usually return void, so you can easily distinguish them from functions that return a different object.
Also, there's no reason to mutableCopy the gpsAttributes dictionary; it's just being used as the argument to -[addEntriesFromDictionary:], which doesn't need to be mutable.
So, the right way to do this is:
NSDictionary *areaAttributes = [[area entity] attributesByName];
NSDictionary *gpsAttributes = [[gps entity] attributesByName];
NSMutableDictionary *combinedAttributes = [areaAttributes mutableCopy];
[combinedAttributes addEntriesFromDictionary:gpsAttributes];
You may want to wrap this up in a function (or a method in a category on NSDictionary), if you do if often:
NSDictionary *mergeDictionaries(NSDictionary *lhs, NSDictionary *rhs) {
NSMutableDictionary *ret = [lhs mutableCopy];
[ret addEntriesFromDictionary:rhs];
return ret;
}
From the Documentation, addEntriesFromDictionary tells that:
If both dictionaries contain the same key, the receiving dictionary’s previous value object for that key is sent a release message, and the new value object takes its place.
You need to use setObject to add each object to the dictionary.YOu need to loop through the keys of one dictionary and add it to the final dictionary.
Even setObject tells the same:
The key for value. The key is copied (using copyWithZone:; keys must conform to the NSCopying protocol). If aKey already exists in the dictionary, anObject takes its place.
You cannot have two same keys in the dictionary. All keys in the dictionary are unique.
If you still want to have the same key-value in the dictionary, you must use a different key.
For example, you have two dictionaries with following values:
NSDictionary *dict1=#{#"hello":#"1",#"hello2" :#"2"};
NSDictionary *dict2=#{#"hello":#"1",#"hello2":#"2",#"hello3":#"1",#"hello6":#"2",#"hello4":#"1",#"hello5" :#"2"};
NSMutableDictionary *mutableDict=[NSMutableDictionary dictionaryWithDictionary:dict1];
for (id key in dict2.allKeys){
for (id subKey in dict1.allKeys){
if (key==subKey) {
[mutableDict setObject:dict2[key] forKey:[NSString stringWithFormat:#"Ext-%#",key]];
}else{
[mutableDict setObject:dict2[key] forKey:key];
}
}
}
and by the end of this loop, your new mutable dictionaries will have the follwoing key-values:
{
"Ext-hello" = 1;
"Ext-hello2" = 2;
hello = 1;
hello2 = 2;
hello3 = 1;
hello4 = 1;
hello5 = 2;
hello6 = 2;
}
As you can see, hello, and hello2 keys are renamed as Ext-hello1, Ext-hello2. form the dict1, and you still have all the dict2 values added to your mutable dict.
IF you don't want to add a new key, then you can add the values into an arrya and add that array to the dictionary. YOu can modify the for-loop to:
for (id key in dict2.allKeys){
for (id subKey in dict1.allKeys){
if (key==subKey) {
NSMutableArray *myArr=[[NSMutableArray alloc]init];
[myArr addObject:dict1[subKey]];
[myArr addObject:dict2[key]];
[mutableDict setObject:myArr forKey:key];
}else{
[mutableDict setObject:dict2[key] forKey:key];
}
}
}
And now you will have the values merged into an array:
{
hello = (
1,
1
);
hello2 = 2;
hello3 = 1;
hello4 = 1;
hello5 = 2;
hello6 = 2;
}
In this way, the number of keys will be same, and the values for the same key will be added as an array.

NSMutableDictionary -- using allKeysforObject not retrieving array values

NSMutableDictionary *expense_ArrContents = [[NSMutableDictionary alloc]init];
for (int i = 1; i<=4; i++) {
NSMutableArray *current_row = [NSMutableArray arrayWithObjects:#"payer_id",#"Expense_Type_id",#"Category_Id",#"SubCategory_Id",nil];
[expense_ArrContents setObject:current_row forKey: [NSNumber numberWithInt:i]];
}
NSArray *newArray = [expense_ArrContents allKeysForObject:#"payer_id"];
NSLog(#"%#",[newArray description]);
i want to get the list of key values containing the particular object which is in the array of values stored in nsmutabledictionary for a particular key.
In the line where you get all the keys ([expense_ArrContents allKeysForObject:#"payer_id"];) you actually get keys for an object that is not in any of the array's items. This #"player_id" is different object than the #"player_id" you added in current_row. In fact, maybe all of your rows have different #"player_id" objects (except if the compiler has made some optimization - maybe it threats that same string literal as one object instead of creating new object for each iteration).
Try creating an NSString object for the #"player_id" which you add to the current_row and then get all the keys for that same object:
NSString* playerId = #"player_id";
for(){
NSMutableArray *current_row = [NSMutableArray arrayWithObjects: playerId,...];
...
}
NSArray *newArray = [expense_ArrContents allKeysForObject:playerId];
Your NSArray *newArray = [expense_ArrContents allKeysForObject:#"payer_id"]; will not return any value because in expense_ArrContents there is no such key(#"payer_id"), instead there are keys like 1,2,3 etc.What is your requirement?Want to see what all keys are there in expense_ArrContents just log
NSArray*keys=[expense_ArrContents allKeys];
Try this :
NSMutableArray *array_key=[[NSMutableArray alloc]init];
for (NSString *key in expense_ArrContents) {
if ([[expense_ArrContents objectForKey:key] containsObject:#"payer_id"]) {
[array_key addObject:key];
}
}

Resources