Updating key in NSMutableDictionary - objective-c

json is a NSMutableDictionary with a key "message". I need to "clean" the data in that key using stringByReplacingOccurrencesOfString
NSLog(#"json: %#", json); outputs this:
json: {
conversationId = 61;
countmessagesinconversation = 2;
message = "Messages exist!";
messagesinconversation = (
{
message = "Hi";
messagecreated = "June 24, 2013 16:16";
messageid = 68;
sentby = Thomas;
sentbyID = 1;
title = "Subject";
},
{
message = "What's up?";
messagecreated = "September 22, 2013 17:00";
messageid = 331;
sentby = Steve;
sentbyID = 2;
title = "Subject";
}
);
success = 1;
}
So, this is what I've come up with, but I'm clearly getting it all wrong.
NSString *newstr = [[NSString alloc]init];
for (newstr in [[[json objectForKey:#"messagesinconversation"]allKeys]copy]){
newstr = [newstr stringByReplacingOccurrencesOfString:#" "
withString:#""];
[json setValue:newstr forKey:#"message"];
}
Could somebody please help me out with this and explain so I understand the concept? I know how to do this with an array but what my problem is (I think) is that I do not fully understand how to access the right key in the dictionary.
Edit: Sorry for not being clear in my question. What happens is that if there are two space characters in the key message then " " shows up when I display it on the screen.

//First get the array of message dictionaries:
NSArray * messages = [json objectForKey:#"messagesinconversation"];
//create new array for new messages
NSMutableArray * newMessages = [NSMutableArray arrayWithCapacity:messages.count];
//then iterate over all messages (they seem to be dictionaries)
for (NSDictionary * dict in messages)
{
//create new mutable dictionary
NSMutableDictionary * replacementDict = [NSMutableDictionary dictionaryWithDictionary:dict];
//get the original text
NSString * msg = [dict objectForKey:#"message"];
//replace it as you see fit
[replacementDict setObject:[msg stringByReplacingOccurrencesOfString:#" " withString:#""] forKey:#"message"];
//store the new dict in new array
[newMessages addObject:replacementDict];
}
//you are done - replace the messages in the original json dict
[json setObject:newMessages forKey:#"messagesinconversation"];

Add your array of dictionary in another mutable array
NSMutableArray *msgArray = [[json objectForKey:#"messagesinconversation"] mutableCopy];
Use for loop for access it.
for (int i = 0 ; i < msgArray.count ; i++)
{
[[[msgArray objectAtindex:i] objectForKey:#"message"] stringByReplacingOccurrencesOfString:#" " withString:#""];
}
[self.mainJSONDic setValue:msgArray forKey:#"messagesinconversation"];
Try this code might helpful in your case:

Now that the requirements are more clear I'll try an answer. I try to use the same names as in your question and suggested in erlier answers.
Make json an NSMUtableDictionary where you declare it. Then go forward:
json = [json mutableCopy]; // creates a mutable dictionary based on json which is immutable as result of the json serialization although declared as mutable.
NSMutableArray *msgArray = [[json objectForKey:#"messagesinconversation"] mutableCopy]; //this fetches the array from the dictinary and creates a mutable copy of it.
[json setValue:newstr forKey:#"messagesinconversation"]; // replace the original immutable with the mutable copy.
for (int i = 0; i < [msgArray count]; i++) {
NSMutableDictionary mutableInnerDict = [[msgArray objectAtIndex:i] mutableCopy]; // fetching the i-th element and replace it by a mutable copy of the dictionary within.
[msgArray replaceObjectAtIndex:i withObject:mutableInnerDict]; // it is now mutable and replaced within the array.
NSString *newString = [[[msgArray objectAtindex:i] objectForKey:#"message"] stringByReplacingOccurrencesOfString:#" " withString:#" "]; // crates a new string with all &nbsp removed with blanks.
[mutableInnerDict setValue:newString forKey:#"message"];
}
Regarding the replacement of &nbsp with " ", is this really what you want? I am asking because &nbsp does not occur in your sample data. Or do you want to remove blanks at all?

Related

addObject replaces the previous values

In the following code i am trying to add the user selected names in arrayDoctors, say A,B and then i add it to selectedDoctors array. But the 2nd time i select doctors C,D and add it to selectedDoctors array it replaces the previous objects A,B to C and D.
- (void)doneDoctorSelection:(id)sender
{
[pop3 dismissPopoverAnimated:YES];
NSString *str = [arrayDoctors objectAtIndex:0];
NSString *str1 = [str stringByAppendingString:[NSString stringWithFormat:#" + %d",arrayDoctors.count-1]];
if([arrayDoctors count] == 1)
lblDoctor.text = str;
else
lblDoctor.text = str1;
[selectedDoctors addObject:arrayDoctors];
[selectedDoctorIdList addObject:arrayDoctorsId];
NSLog(#"selectedDoneDoctors %# ",selectedDoctors);
}
What am i doing wrong?
Instead of this part
NSMutableArray *tempDoctor = [[NSMutableArray alloc]initWithArray:arrayDoctors];
[selectedDoctors addObject:tempDoctor];
You can use
[selectedDoctors addObject:[arrayDoctors mutableCopy]];
Ah!! finally found the solution. I was removing the objects from arrayDoctors before putting new values in it, and i had initialized it in viewDidLoad method. So what was happening is when i did [selectedDoctors addObject:arrayDoctors]; the second time it was replacing the same object at 1st place also since its uses the same memory location. The solution was to create a temporary object. The following is the changed code.
- (void)doneDoctorSelection:(id)sender
{
[pop3 dismissPopoverAnimated:YES];
NSString *str = [arrayDoctors objectAtIndex:0];
NSString *str1 = [str stringByAppendingString:[NSString stringWithFormat:#" + %d",arrayDoctors.count-1]];
NSMutableArray *tempDoctor = [[NSMutableArray alloc]initWithArray:arrayDoctors];
NSMutableArray *tempDocorId = [[NSMutableArray alloc]initWithArray:arrayDoctorsId
];
if([arrayDoctors count] == 1)
lblDoctor.text = str;
else
lblDoctor.text = str1;
NSLog(#"Before Adding %# ",selectedDoctors);
[selectedDoctors addObject:tempDoctor];
[selectedDoctorIdList addObject:tempDocorId];
//[selectedDoctors addObjectsFromArray:arrayDoctors];
//[selectedDoctorIdList addObjectsFromArray:arrayDoctorsId];
NSLog(#"selectedDoneDoctors %# ",selectedDoctors);
}
Thanx for pointing me in right direction.

All objects in array (interpreted from csv) being returned as the same object (the last object)

What I am trying to achieve, is to convert a csv file into an array of custom objects, however, my attempts at this seem to result in all of the objects in the array being returned as the same object (the last object in the array).
Before I explain further, here is the code:
- (NSArray *)arrayFromCSVFileName:(NSString *)csvFileName fileType:(NSString *)fileType {
// Convert the file into an NSData object
NSString *studentFilePath = [[NSBundle mainBundle] pathForResource:csvFileName ofType:fileType];
NSData *studentData = [NSData dataWithContentsOfFile:studentFilePath];
// Convert the NSData into an NSString
NSString *csvString = [[NSString alloc] initWithData:studentData encoding:NSUTF8StringEncoding];
// Split each record (line) in the csvDataString into an individual array element (split on the newline character \n)
NSArray *csvArray = [csvString componentsSeparatedByString:#"\n"];
// Create an array to hold the parsed CSV data
NSMutableArray *parsedCSVArray = [[NSMutableArray alloc] init];
NSMutableArray *elementArray = [[NSMutableArray alloc] init];
CGSElement *elementToAdd = [[CGSElement alloc] init];
// Loop through each line of the file
for (int i = 0; i < [csvArray count]; i++) {
// Get a reference to this record (line) as a string, and remove any extranous new lines or alike
NSString *csvRecordString = [[csvArray objectAtIndex:i] stringByReplacingOccurrencesOfString:#"\r" withString:#""];
// Split the line by the comma delimeter
NSArray *csvRecordArray = [csvRecordString componentsSeparatedByString:#","];
// Check that there are actually fields (i.e. this is not a blank line)
if ( ([csvRecordArray count] > 0) && ([[csvRecordArray objectAtIndex:0] length] > 0) ) {
elementToAdd.mass = [[csvRecordArray objectAtIndex:1] floatValue];
elementToAdd.atomicNumber = [[csvRecordArray objectAtIndex:0] intValue];
elementToAdd.name = [csvRecordArray objectAtIndex:2];
elementToAdd.symbol = [csvRecordArray objectAtIndex:3];
elementToAdd.period = [[csvRecordArray objectAtIndex:4] intValue];
[elementArray addObject:elementToAdd];
}
}
for (int i = 0; i < [elementArray count]; i++) {
NSLog(#"%i", i);
CGSElement *current = [elementArray objectAtIndex:i];
NSLog(#"Name = %#", current.name);
}
// Return the parsed array
return elementArray;
}
The custom object in question is the CGSElement object, which I am attempting to fill the elementArray with. However, my debug code (the following section of code):
for (int i = 0; i < [elementArray count]; i++) {
NSLog(#"%i", i);
CGSElement *current = [elementArray objectAtIndex:i];
NSLog(#"Name = %#", current.name);
}
Is resulting, rather than in the return of all of the correct element names, it is returning the last element (to put this in context, ununoctium), 118 times.
After some testing, I can safely say that up until after this point:
elementToAdd.mass = [[csvRecordArray objectAtIndex:1] floatValue];
elementToAdd.atomicNumber = [[csvRecordArray objectAtIndex:0] intValue];
elementToAdd.name = [csvRecordArray objectAtIndex:2];
elementToAdd.symbol = [csvRecordArray objectAtIndex:3];
elementToAdd.period = [[csvRecordArray objectAtIndex:4] intValue];
All of the elements are being correctly defined, rather than just the same element over and over.
Needless to say, I'm stumped as to why it would be returning the same object over and over. Any help would be appreciated.
This line:
CGSElement *elementToAdd = [[CGSElement alloc] init];
Should be inside your loop, just before you try to edit the object and add it to the array. Currently you are repeatedly mutating the same object instead of creating new objects for each record.
You add the same entity all the time. It is crated once before the loop and within the loop it values are changed again and angan and it is added to the array. Naturally all items in the aray carry the same values because it is the same object.
If you want then change the array with an NSSet. To a set an object can only added once and you will end up with a set of 1. That is not the solution of couse, it would just visualize what is happening.
To solve it move this line
CGSElement *elementToAdd = [[CGSElement alloc] init];
to the beginning of the body of the for i loop, so that a new instance is created for every iteration and therefore for every index of the array.

how to use nsdictionary to split strings and swap values

I'm new to ios, trying to do something like this
self.dataArray = [dictionary valueForKey:#"LIST"];
{
"List": [
{
"FULLNAME": "FirstName, LastName",
"ID": "281"
}
]
}
I want to swap the value which is in 'FULLNAME', that is I want it to be LastName, FirstName
I tried various methods but it would not work. Can someone tell me how to accomplish this?
Thank you
Assuming you know how to do the dictionary manipulation, here's one way to swap the elements in the string:
NSString* name = #"Firstname, Lastname";
// Remove the space after comma (not necessary if you know the name will always
// have a space after comma, then just split on ', '.
NSString* normalizedName = [name stringByReplacingOccurrencesOfString:#", " withString:#","];
// Split the string on ','.
NSArray* nameParts = [normalizedName componentsSeparatedByString:#","];
// Reverse the array
NSArray* reverseNameParts = [[nameParts reverseObjectEnumerator] allObjects];
// Join the array with ', '
NSString* revname = [reverseNameParts componentsJoinedByString:#", "];
NSLog(#"Reversed name parts: %#", revname);
I believe it should be objectForKey instead of valueForKey, if your dictionary contains arrays.
self.dataArray = [dictionary objectForKey:#"LIST"];
for(NSString *fullname in self.dataArray){
if([fullname isEqualToString:#"FULLNAME"]){
NSArray *name = [fullname componentsSeparatedByString:#","];
NSMutableString* reverseName = [NSMutableString string];
for (int i=[name count]-1; i>=0;i--){
[reverseName appendFormat:#", ", [name objectAtIndex:i]];
//reverse name should be what you are looking for..or something close
}
}
}
hope this helps

splitting nsstring and putting into tableview

I am trying to split the string into parts and insert into a table how should i do it?
I got an error for splitting of the array which is: -[__NSArrayI componentsSeparatedByString:]: unrecognized selector sent to instance 0x7a421e0
NSArray *BusRoute = alightDesc;
int i;
int count = [BusRoute count];
for (i = 0; i < count; i++)
{
NSDictionary *dic = [BusRoute objectAtIndex: i];
NSDictionary *STEPS = [dic valueForKey:#"STEPS"];
NSString *AlightDesc = [STEPS valueForKey:#"AlightDesc"];
NSLog(#"AlightDesc = %#", AlightDesc);
NSArray *aDescArray = [AlightDesc componentsSeparatedByString:#","];
NSLog(#"aDescArray = %#", aDescArray);
}
This is the string which I'm splitting, i got it from the NSLog
AlightDesc = (
"Block1",
"Block2",
"Block3"
)
please help I'm stuck thanks.
Objective C is not a strongly typed language. All you know for sure about [STEPS valueForKey:#"AlightDesc"] is that it will return an object (of type id). When you wrote NSString *AlightDesc = [STEPS valueForKey:#"AlightDesc"] the compiler did not complain because NSString * is a valid object type. Unfortunately there is a logic error in your code so that what was actually stored under the key #"AlightDesc" is an NSArray. As others have mentioned, NSArray does not respond to componentsSeparatedByString: so you get an error at runtime.
The easy fix for this is to correct your logic: Either store an NSString in the first place or treat what you get out as an NSArray. As #janusfidel mentioned you can use an NSArray perfectly well in a table by using objectAtIndex: to get the string for the entry you want.
In some more complicated cases you may not know what you will be getting out of a dictionary for a particular key. In that case in Objective C you can just ask the object:
id anObject = [STEPS valueForKey:#"AlightDesc"];
if ([anObject isKindOfClass:[NSString class]]) {
NSString *aString = (NSString *)anObject;
// Treat as a string ...
} else if ([anObject isKindOfClass:[NSString class]]) {
// Object is an array ...
Your NSString *AlightDesc should look like this
NSString *AlightDesc = "Block1,Block2,Block3";
NSArray *aDescArray = [AlightDesc componentsSeparatedByString:#","];
If your string is what you say it is
AlightDesc = ("Block1","Block2","Block3");
then your string is the problem because it's already broken up.

NSMutableDictionary sets objects incorrectly

I have a singleton class where I set up a NSMutableDictionary called completedLevels.
This is how I set it up (in the init method of my singleton):
NSString *mainPath = [[NSBundle mainBundle] bundlePath];
NSString *levelConfigPlistLocation = [mainPath stringByAppendingPathComponent:#"levelconfig.plist"];
NSDictionary *levelConfig = [[NSDictionary alloc] initWithContentsOfFile:levelConfigPlistLocation];
completedLevels = [[NSMutableDictionary alloc]init];
NSMutableDictionary *levelSets = [[NSMutableDictionary alloc]init];
NSMutableDictionary *levels = [[NSMutableDictionary alloc]init];
NSMutableDictionary *stats = [[NSMutableDictionary alloc]init];
[stats setObject:[NSNumber numberWithBool:NO] forKey:#"levelDone"];
[stats setObject:[NSNumber numberWithInt:0] forKey:#"stars"];
[stats setObject:[NSNumber numberWithInt:0] forKey:#"time"];
[stats setObject:[NSNumber numberWithInt:0] forKey:#"bestTime"];
for (int i = 1; i<=18; i++) {
[levels setObject:stats forKey:[NSString stringWithFormat:#"level%d", i]];
}
for(int i= 1; i<=15;i++){
NSString *lvlSet = [NSString stringWithFormat:#"levelSet%d", i];
[levelSets setObject:levels forKey:lvlSet];
}
NSArray *categoriesArray = [levelConfig objectForKey:#"categoriesArray"];
for (int i=0; i<[categoriesArray count]; i++) {
NSString *category = [[levelConfig objectForKey:#"categoriesArray"]objectAtIndex:i];
[completedLevels setObject:levelSets forKey:category];
}
I want to explain my doings:
My intention was to create a dictionary in this form:
category = {
levelSet1 ={
level1 ={
bestTime = 0;
levelDone = 0;
stars = 0;
time = 0;
};
level2={
bestTime = 0;
levelDone = 0;
stars = 0;
time = 0;
};
.
.
.
}
levelSet2 ={
level1 ={
bestTime = 0;
levelDone = 0;
stars = 0;
time = 0;
};
level2={
bestTime = 0;
levelDone = 0;
stars = 0;
time = 0;
};
.
.
.
}
.
.
.
}
%d in the case of levelSet are integers from 1 to 15.
%d in the case of level are integers from 1 to 18.
I have several categories, and thus multiple sets of the example above.
This works well and upon calling NSLog, the dictionary appears in my console as it should.
The problem, however, arises when I want to change some entries in my dictionary as shown in the example below:
NSString *category = [[GameStateSingleton sharedMySingleton]getCurrentCategory];
NSString *levelSet = [NSString stringWithFormat:#"levelSet%d",[[GameStateSingleton sharedMySingleton]getSharedLevelSet]];
NSNumber *currentLevel = [NSNumber numberWithInt:[[GameStateSingleton sharedMySingleton]getSharedLevel]];
NSString *levelString = [NSString stringWithFormat:#"level%d", [currentLevel intValue]];
NSMutableDictionary *categories = [[NSMutableDictionary alloc]initWithDictionary:
[[GameStateSingleton sharedMySingleton]getCompletedLevels]];
[[[[categories objectForKey:category]objectForKey:levelSet]objectForKey:levelString]setObject:[NSNumber numberWithBool:YES] forKey:#"levelDone"];
[[GameStateSingleton sharedMySingleton]setCompletedLevels:categories];
NSLog(#"%#",[[GameStateSingleton sharedMySingleton]getCompletedLevels]);
To explain that:
When the player is done with a level, I want the levelDone entry to change its value. But when I log it afterwards, suddenly all levelDone entries of all categories change to the BOOLEAN value of 1. Why is that ?
------------------- update -----------------------
completedLevels = [[NSMutableDictionary alloc]init];
NSMutableDictionary *levelSets = [[NSMutableDictionary alloc]init];
NSMutableDictionary *levels = [[NSMutableDictionary alloc]init];
NSMutableDictionary *stats = [[NSMutableDictionary alloc]init];
[stats setObject:[NSNumber numberWithBool:NO] forKey:#"levelDone"];
[stats setObject:[NSNumber numberWithInt:0] forKey:#"stars"];
[stats setObject:[NSNumber numberWithInt:0] forKey:#"time"];
[stats setObject:[NSNumber numberWithInt:0] forKey:#"bestTime"];
for (int i = 1; i<=18; i++) {
NSMutableDictionary *statsCopy = [stats mutableCopy];
[levels setObject:statsCopy forKey:[NSString stringWithFormat:#"level%d", i]];
[statsCopy release];
}
for(int i= 1; i<=15;i++){
NSString *lvlSet = [NSString stringWithFormat:#"levelSet%d", i];
NSMutableDictionary *levelsCopy = [levels mutableCopy];
[levelSets setObject:levelsCopy forKey:lvlSet];
[levelsCopy release];
}
NSArray *categoriesArray = [levelConfig objectForKey:#"categoriesArray"];
for (int i=0; i<[categoriesArray count]; i++) {
NSString *category = [[levelConfig objectForKey:#"categoriesArray"]objectAtIndex:i];
NSMutableDictionary *levelSetsCopy = [levelSets mutableCopy];
[completedLevels setObject:levelSetsCopy forKey:category];
[levelSetsCopy release];
}
The part where I retrieve and set it stayed the same...
_________________ SOLUTION ____________________
NSMutableDictionary *mutableCopy = (NSMutableDictionary *)CFPropertyListCreateDeepCopy(kCFAllocatorDefault, (CFDictionaryRef)originalDictionary, kCFPropertyListMutableContainers);
I made deep-copies with this method.
Basically, the problem is that you are setting the level dictionary for each level to the same stats object:
for (int i = 1; i<=18; i++) {
[levels setObject:stats forKey:[NSString stringWithFormat:#"level%d", i]];
}
Instead, you need to set them each to a different mutable copy of the object:
for (int i = 1; i<=18; i++) {
NSMutableDictionary *statsCopy = [stats mutableCopy];
[levels setObject:statsCopy forKey:[NSString stringWithFormat:#"level%d", i]];
[statsCopy release];
}
That should solve the first part for you. However, you also have the same problem with the levelSets. Basically, whenever you add a mutable dictionary, you need to determine whether you are trying to point at the same mutable object (you want to keep them synchronized), or whether you want copies of that mutable object (thus, they act independently). And when you look at building this kind of tree structure, that means you need to look at this at every level.
So, looking at the rest of your code, you'll also need to fix your level sets and categories in the same way, by making a mutable copy inside of each loop and then adding that mutable copy instead of adding the original object. And, since you want to mutate the contents of the contents of the dictionaries, every time you make a mutable copy that contains another mutable dictionary, you need to make that copy at each depth.
You are going to have to choose between building these things depth-wise or doing a deep copy. There's another question:
deep mutable copy of a NSMutableDictionary
That covers deep copies of mutable dictionaries pretty well. Alternatively, you can invert the building of the structure so that instead of making copies, you build each dictionary except for stats which you can easily copy.
The deep copy creates copies of each of the mutable entries all the way down to the farthest leaf nodes. If you think about the NSMutableDictionaries as a tree with the base of the tree at the top ( completedLevels in your case) and the leaves as the elements of the stats NSMutableDictionaries that you copy, you want to individually manipulate every element of the tree, whether it is the leaf node or any intermediate.
An illustration could be made as follows, with this representing the contents of MyDictionary:
Top-A:
Second-A-1
Second-A-2
Top-B:
Second-B-1
Second-B-2
To recap, you have the MyDictionary at the top, which has 2 keys (Top-A,Top-B), each of which is associated with a separate NSMutableDictionary object. Each of those NSMutableDictionaries has 2 keys, each associated with a separate object.
When you make a -mutableCopy of MyDictionary, the result is a new NSMutableDictionary with 2 keys (Top-A,Top-B), each associated with an NSMutableDictionary. However, these NSMutableDictionary objects are actually the same objects as the ones in MyDictionary. This is a shallow copy, meaning that there is a new top-level object (the mutable copy of MyDictionary), but that objects associated with each of the keys in the new dictionary are the same objects as were associated with the keys in MyDictionary. As such, if you change Top-A in the copy be associated with a different NSMutableDictionary, then the Top-A in MyDictionary and the copy will no longer be the same. But, if you change the value associated with Second-A-1, it will change both in MyDictionary and the copy of MyDictionary because Top-A points at a single object.
When you make a deep copy, the utility copies every element of every dictionary individually, which means that the copy of MyDictionary will have a separate Top-A, Second-A-1, Second-A-2,Top-B, etc. from the ones that exist in MyDictionary, and therefore, you can change the values of any of the dictionaries (no matter how deep) without fear of changing other objects.