How to switch two values in an NSDictionary in Objective-C? - objective-c

I have an NSDictionary with two values. Now I want to switch the values. Here is my code:
NSDictionary *aDictionary = [[NSDictionary alloc] init];
[aDictionary setValue:#"theValue1" forKey:#"theKey1"];
[aDictionary setValue:#"theValue2" forKey:#"theKey2"];
NSString *tmpValue = [aDictionary objectForKey:#"theKey1"];
[aDictionary setValue:[myDict objectForKey:#"theKey2"] forKey:#"theKey1"];
[aDictionary setValue:tmpValue forKey:[myDict objectForKey:#"theKey2"]];
NSLog(#"%#", myDict);
//output
theKey1 = theValue2;
theKey2 = theValue2;
theValue2 = theValue1;
What am I doing wrong?

Other than a couple of larger problems (you should be using NSMutableDictionary instead of NSDictionary if you want to change things, and you should use the setObject:forKey: method instead of the setValue:forKey: method), the root of your problem is this line:
[aDictionary setValue:tmpValue forKey:[myDict objectForKey:#"theKey2"]];
Think about what key you're setting.

Related

Xcode Sort Arrays by size in objectKey

So I have an object of type 'id friendData'stored in a singleton class 'coreData' from which I produce a mutable array for object key "data" as follows:
NSMutableArray *friends = [_coreData.friendData objectForKey:#"data"];
I then establish a dictionary which takes user parameters using keys "id" and "name", as well as a NSMutable array *scores which is obtained by a HTTP post request.
NSMutableDictionary *directory = [NSMutableDictionary dictionary];
[directory setObject:[friends valueForKey:#"id"] forKey:#"id"];
[directory setObject:[friends valueForKey:#"name"] forKey:#"name"];
[directory setObject:scores forKey:#"score"];
I am wanting to order object scores from highest to lowest for the purposes of a scoreboard, but it's my understanding that rearranging within a dictionary won't maintain the same order for the objects 'id' and 'name'. Is this infact possible, or is it better to reintroduce the *scores object to *friends under an appropriate key, and apply a sort algorith? If so, how? Any help, including example code and possible sort procedure would be great!
Just sort scores array before setting it into the dictionary. I am not sure what are exactly the kind of objects you have in scores, Im guessing they are plain NStrings. In that case this should work:
NSArray *sortedArray = [scores sortedArrayUsingComparator:^NSComparisonResult(id a, id b) {
NSInteger first = [(NSString *)a intValue];
NSDate *second = [(NSString *)b intValue];
return [first compare:second];
}];
NSMutableDictionary *directory = [NSMutableDictionary dictionary];
[directory setObject:[friends valueForKey:#"id"] forKey:#"id"];
[directory setObject:[friends valueForKey:#"name"] forKey:#"name"];
[directory setObject:sortedScoresArray forKey:#"score"];

Why is this string immutable?

Why does the code give the error - Attempt to mutate immutable object with appendFormat: ?
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
for (NSTextTestingResult *match in matches) {
<omitted>
NSMutableString *value;
value = (NSMutableString *)[response stringWithRange:range];
if ([dict objectForKey:#"traveler"])
[dict objectForKey:#"traveler"] appendFormat:#"%#", value]; // Errors here
[dict setObject:value forKey:key];
}
Value is being created as a _NSCFString.
Because [response stringWithRange:range] returns an immutable NSString *, and casting doesn't make it become mutable.
You want value = [[response stringWithRange:range] mutableCopy];.
Note that if you're not using ARC, you need to remember to release the mutableCopy. Although the return value of [response stringWithRange:range] is autoreleased, the mutableCopy is not.
I dont think you can cast a string to mutable like that.
You need to do it like this
ms = [[NSMutableString alloc] init];
[ms setString:immutableString];
Oops wrong again the way the subclass works you should be able to do it like this more simply.
ms = [NSMutableString stringWithString: immutableString];

Sort NSMutableArray based on strings from another NSArray

I have an NSArray of strings that I want to use as my sort order:
NSArray *permissionTypes = [NSArray arrayWithObjects:#"Read", #"Write", #"Admin", nil];
I then have a NSMutableArray that may or may not have all three of those permissions types, but sometimes it will only be 2, sometimes 1, but I still want it sorted based on my permissionsTypes array.
NSMutableArray *order = [NSMutableArray arrayWithArray:[permissions allKeys]];
How can I always sort my order array correctly based on my using the permissionTypes array as a key?
I would go about this by creating a struct or an object to hold the permission types.
Then you can have...
PermissionType
--------------
Name: Read
Order: 1
PermissionType
--------------
Name: Write
Order: 2
and so on.
Then you only need the actual array of these objects and you can sort by the order value.
[array sortUsingComparator:^NSComparisonResult(PermissionType *obj1, PermissionType *obj2) {
return [obj1.order compare:obj2.order];
}];
This will order the array by the order field.
NSMutableArray *sortDescriptors = [NSMutableArray array];
for (NSString *type in permissionTypes) {
NSSortDescriptor *descriptor = [[[NSSortDescriptor alloc] initWithKey:type ascending:YES] autorelease];
[sortDescriptors addObject:descriptor];
}
sortedArray = [myArray sortedArrayUsingDescriptors:sortDescriptors];
Use whichever sorting method on NSMutableArray you prefer, you will either provide a block or a selector to use for comparing two elements. In that block/selector rather than comparing the two strings passed in directly look each up in your permissionTypes array using indexOfObject: and compare the resulting index values returned.
I suggest you another approuch:
- (void)viewDidLoad
{
[super viewDidLoad];
arrayPermissions = [[NSMutableArray alloc] init];
NSDictionary *dicRead = [NSDictionary dictionaryWithObjectsAndKeys:
#"Read", #"Permission", nil];
NSDictionary *dicWrite = [NSDictionary dictionaryWithObjectsAndKeys:
#"Write", #"Permission", nil];
NSDictionary *dicAdmin = [NSDictionary dictionaryWithObjectsAndKeys:
#"Admin", #"Permission", nil];
NSLog(#"my dicRead = %#", dicRead);
NSLog(#"my dicWrite = %#", dicWrite);
NSLog(#"my dicAdmin = %#", dicAdmin);
[arrayPermissions addObject:dicRead];
[arrayPermissions addObject:dicWrite];
[arrayPermissions addObject:dicAdmin];
NSLog(#"arrayPermissions is: %#", arrayPermissions);
// create a temporary Dict again
NSDictionary *temp =[[NSDictionary alloc]
initWithObjectsAndKeys: arrayPermissions, #"Permission", nil];
// declare one dictionary in header class for global use and called "filteredDict"
self.filteredDict = temp;
self.sortedKeys =[[self.filteredDict allKeys]
sortedArrayUsingSelector:#selector(compare:)];
NSLog(#"sortedKeys is: %i", sortedKeys.count);
NSLog(#"sortedKeys is: %#", sortedKeys);
}
hope help

NSDictionary setValue:forKey: -- getting "this class is not key value coding-compliant for the key"

I have this simple loop in my program:
for (Element *e in items)
{
NSDictionary *article = [[NSDictionary alloc] init];
NSLog([[e selectElement: #"title"] contentsText]);
[article setValue: [[e selectElement: #"title"] contentsText] forKey: #"Title"];
[self.articles insertObject: article atIndex: [self.articles count]];
[article release];
}
It is using the ElementParser library to make a dictionary of values from an RSS feed (there are other values besides "title" which I have omitted). self.articles is an NSMutableArray which is storing all of the dictionaries in the RSS document.
In the end, this should produce an array of dictionaries, with each dictionary containing the information I need about the item at any array index. When I try to use setValue:forKey: it gives me the
this class is not key value coding-compliant for the key "Title"
error. This has nothing to do with Interface Builder, it is all code-only. Why am I getting this error?
First off, you're using -setValue:forKey: on a dictionary when you should be using -setObject:forKey:. Secondly, you're trying to mutate an NSDictionary, which is an immutable object, instead of an NSMutableDictionary, which would work. If you switch to using -setObject:forKey: you'll probably get an exception telling you that the dictionary is immutable. Switch your article initialization over to
NSMutableDictionary *article = [[NSMutableDictionary alloc] init];
and it should work.
This:
NSDictionary *article = [[NSDictionary alloc] init];
means that the dictionary is immutable. If you want to change its contents, create a mutable dictionary instead:
NSMutableDictionary *article = [[NSMutableDictionary alloc] init];
Alternatively, you could create your dictionary as:
NSDictionary *article = [NSDictionary dictionaryWithObject:[[e selectElement: #"title"] contentsText] forKey:#"Title"];
and drop the release at the end of that method.
Also, the canonical method to add/replace objects in a (mutable) dictionary is -setObject:forKey:. Unless you are familiar with Key-Value Coding, I suggest you don’t use -valueForKey: and -setValue:forKey:.

I am typing in text from a book for NSDictionary and get an error from it?

I typed in text from a book and
I get this error: Passing argument of 1 of "initWithObjects:forKeys:count:" from incompatible pointer type
NSDictionary *dict = [[NSDictionary alloc] initWithObjects: #"hello", #"there", #"persn"
forKeys: #"aa", #"bb", #"cc"
count: 3 ];
NSLog(#"%#", [dict objectForKey: #"bb"]);
In Objective-C, methods can't use var-args like that, they must always come at the end of the invocation.
In fact, the parameters to your message invocation are actually pointers to buffers of objects and keys.
Try this:
id objects[] = {#"hello", #"there", #"person"};
id keys[] = {#"aa", #"bb", #"cc"};
NSDictionary *dict1 = [[NSDictionary alloc] initWithObjects:objects forKeys:keys count:3];
NSLog(#"%#", [dict1 objectForKey: #"bb"]);