Xcode Sort Arrays by size in objectKey - objective-c

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"];

Related

How can I swap keys and values in NSDictionary?

I want to know how to invert a NSDictionary.
I've seen some crazy code like
NSDictionary *dict = ...;
NSDictionary *swapped = [NSDictionary dictionaryWithObjects:dict.allKeys forKeys:dict.allValues];
which is according to documentation not safe at all since the order of allValues and allKeys is not guaranteed.
NSDictionary *dict = ...;
NSMutableDictionary *swapped = [NSMutableDictionary new];
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
swapped[value] = key;
}];
Note that the values should also conform to the NSCopying protocol.
You're right, that code is crazy, but there are two ways to get an array of the values in the order given by an array of keys:
NSArray * keys = [dict allKeys];
NSArray * vals = [dict objectsForKeys:keys notFoundMarker:nil];
NSDictionary * inverseDict = [NSDictionary dictionaryWithObjects:keys
forKeys:vals];
Or
NSUInteger count = [dict count];
id keys[count];
id vals[count];
[dict getObjects:vals andKeys:keys];
NSDictionary * inverseDict = [NSDictionary dictionaryWithObjects:keys
forKeys:vals
count:count];
The former is obviously a lot nicer. As noted in hfossli's answer, the objects that were values in the original dictionary must conform to NSCopying in order to be used as keys in the inversion.

Sorting JSON Data by date

Hi I'm getting data online from a JSON file and I'm trying to sort it by date in descending order, I've done it before using an XML parser using an RSS feed and tried to use the same concept but can't seem to get it and it crashes every time.
NSData *data = [NSData dataWithContentsOfURL:url];
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSDictionary *dataDict = [dict objectForKey:#"data"];
NSArray *array = [dataDict objectForKey:#"items"];
for (int i=0; i<[array count]; i++) {
SongInfo *song = [[SongInfo alloc]init];
NSMutableDictionary *entry = [array objectAtIndex:i];
song.uploaded = [entry objectForKey:#"uploaded"];
song.uploader = [entry objectForKey:#"uploader"];
NSComparator comparator = ^(NSDictionary *a, NSDictionary *b) {
return [a[#"uploaded"] compare:b[#"uploaded"]];
};
NSUInteger index = [songsArray indexOfObject:entry
inSortedRange:NSMakeRange(0, [songsArray count])
options:NSBinarySearchingInsertionIndex
usingComparator:comparator];
[songsArray insertObject:song atIndex:index];
EDIT: Managed to fix it by using the NSSortDescriptor and putting it into an array and then back into the same array, not sure if there is a better way to do this but this is how I did it...
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"uploaded"
ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray;
sortedArray = [songsArray sortedArrayUsingDescriptors:sortDescriptors];
songsArray = [NSMutableArray arrayWithArray:sortedArray];
// [songsArray addObject:[song copy] atIndex:index];
[songsArray addObject:song];
You're comparing objects inserted in the songsArray with a comparator that takes in two dictionaries. It seems like the comparator should compare SongInfo objects rather than NSDictionaries.
We can't see what songsArray is. Creating a sorted array manually like you do is really inefficient. Call the NSArray method sortedArrayUsingComparator instead, and there is no need to create SongInfo objects.
On the other hand, if an array of SongInfo* is what you want, create that array first as an NSMutableArray with all the SongInfo's from the JSON data, then sort that array by calling sortUsingComparator.
Note that in the comparator block you can actually use the type of the object: So in the first case NSDictionary* instead of id, in the second case SongInfo* instead of id.

How to detect if the NSDictionary item is an integer?

I have a NSDictionary object with 2 items, the first one is a NSString and the second is an Integer. When I loop into the dictionary items I'd like detect what of they is an Integer.
What is the best way to do it?
The current dictionary is:
[[NSDictionary alloc] initWithObjectsAndKeys:#"San", #"name", #"123", #"id", nil]
The item you are putting in the dictionary is in no way an integer, it's a NSString which only contains numbers. Why not just use a NSNumber object and use it the way it should be?
[[NSDictionary alloc] initWithObjectsAndKeys:#"San", #"name", #123, #"id", nil]
This uses a literal for a NSNumber.
You can use isKindOfClass: to check if an object is of a specific class and enumerateKeysAndObjectsUsingBlock: to analyze every object contained in a dictionary.
For example:
NSDictionary *dictionary = #{#"name": #"San", #"id": #123};
[dictionary enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if ([obj isKindOfClass:[NSNumber class]])
NSLog(#"%#: %# is a number", key, obj);
else
NSLog(#"%#: %# is NOT a number", key, obj);
}];
The first line is a NSDictionary creation using literals, the same is for #123, which automatically insert a NSNumber with 123 value in the dictionary.

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

can't get value from NSDictionary which expected to have NSNumber as output

I have created a NSDictionary like following :
NSArray * alphabets = [NSArray arrayWithObjects:#"a",#"b",#"c",#"d",#"e",#"f",#"g",#"h",#"i",#"j",#"k",#"l",#"m",#"n",#"o",#"p",#"q",#"r",#"s",#"t",#"u",#"v",#"w",#"x",#"y",#"z",nil];
alphaToNum = [NSMutableDictionary dictionary];
numToAlpha = [NSMutableDictionary dictionary];
for(NSString* character in alphabets)
{
[alphaToNum setObject:[NSNumber numberWithInteger:index] forKey:character];
[numToAlpha setObject:character forKey:[NSNumber numberWithInteger:index]];
index++;
}
now I want to access to "numToAlpha" like following :
NSInteger code1;
NSNumber * nn = [numToAlpha objectForKey:code1];
and I'll get error whereas in manual for objectforkey it clearly said (id)objectforkey(id) which means anything!
NSInteger is not an object. id refers to any object type. You need to use [NSNumber numberWithInteger:code1] as you are already doing in your code sample.