This question already has answers here:
How do I sort an NSMutableArray with custom objects in it?
(27 answers)
Closed 8 years ago.
I have array of dictionaries that have NSNumber for key #"id". I want to sort this array based on #"id" value. How can I do it?
You can sorting dictionary using NSSortDescriptor. Please try this :
// Example array containing three dictionaries with "id" and "name" keys
NSArray *unsortedArray = #[#{ #"id":#3, #"name":#"abc"}, #{ #"id":#1, #"name":#"123" }, #{ #"id": #2, #"name":#"xyz" }];
NSLog(#"Unsorted Array === %#", unsortedArray);
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc]
initWithKey: #"id" ascending: YES];
NSArray *sortedArray = [unsortedArray sortedArrayUsingDescriptors: [NSArray arrayWithObject:sortDescriptor]];
NSLog(#"Sorted Array ==== %#", sortedArray);
You can do this easily with -[NSArray sortedArrayUsingComparator:] and a comparison block.
The comparison block needs to return NSComparisonResult. Fortunately, your values associated with key "id" are NSNumbers, so simply return the result of -[NSNumber compare:].
// Example array containing three dictionaries with "id" keys
NSArray *unsortedArray = #[#{ #"id": #3 }, #{ #"id": #1 }, #{ #"id": #2 }];
NSArray *sortedArray = [unsortedArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [obj1[#"id"] compare:obj2[#"id"]];
}];
NSArray is immutable so to sort your array you can replace it with a sorted version like this:
NSArray * myArray = SortArray(myArray);
// SortArray works like this.
// Helper function.
NSInteger MyComparisonFunction(id a, id b, void* context) {
NSNumber *aNum = (NSNumber*)a[#"id"];
NSNumber *bNum = (NSNumber*)b[#"id"];
return [aNum compare:bNum];
}
NSArray* SortArray (NSArray* unsorted) {
return [unsorted sortedArrayUsingFunction:MyComparisonFunction context:nil];
}
Another method would be to instead use an NSMutableArray and then sort that in a similar way.
Related
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);
}
I have an array of dictionaries that looks something like this
({order:1; name:foo},{order:0; name:bar}...)
And I want to order this NSArray of dictionaries by the key "order".
I know I am going to have to loop though it and then add it to a new array but Im not quite sure how to set it up?
Thanks for the help
There are several possible ways to do this.
Here's one:
NSArray *unsortedArray = ... // your unsorted array of dictionaries
NSSortDescriptor *sortBy = [NSSortDescriptor sortDescriptorWithKey:#"order" ascending:YES];
NSArray *sorted = [unsortedArray sortedArrayUsingDescriptors:#[ sortBy ]];
Here's another:
NSArray *unsortedArray = ... // your unsorted array of dictionaries
NSArray *sorted = [unsortedArray sortedArrayUsingComparator:^NSComparisonResult(NSDictionary *dic1, NSDictionary *dic2) {
NSNumber *order1 = dic1[#"order"];
NSNumber *order2 = dic2[#"order"];
return [order1 compare:order2];
}];
I have an array of strings like such:
NSArray *unsortedArray = [NSArray arrayWithObjects:#"MailIn", #"Branch", #"eSign", #"RDC", nil];
I receive this array and it is not gauranteed that all 4 strings will be present. It could have all 4, or only 1.
I need to sort the array in this exact order all the time.. [eSign, RDC, Branch, MailIn]. Keep in mind that there is a chance where one or more of these may not be present. How do I achieve this?? Any help would be greatly appreciated. Thanks in advance!
If you really want a sort then here is a way to do that. But for only 4 strings I'd likely just unroll #Zev Eisenberg's loop and build it up in code.
NSDictionary* indexes = #{ #"eSign" : #0, #"RDC" : #1, #"Branch" : #2, #"MailIn" : #3 };
NSArray* sorted = [unsorted sortedArrayUsingComparator: ^NSComparisonResult(NSString* s1, NSString* s2) {
return [indexes[s1] compare: indexes[s2]];
}];
I would build a new array, using the exemplar array as a template:
NSArray *exemplarArray = #[ #"eSign", #"RDC", #"Branch", #"MailIn" ];
NSArray *unsorted = #[ #"MailIn", #"Branch", #"eSign", #"RDC" ];
NSMutableArray *sorted = [NSMutableArray array];
for (NSString *string in exemplarArray) {
if ([unsorted containsObject:string]) {
[sorted addObject:string];
}
}
NSLog(#"%#", sorted);
I have array which has dictionaries. each dictionary is :
NSDictionary *imageAndIndex=[[NSDictionary alloc] initWithObjectsAndKeys:image,[NSNumber numberWithLong:index], nil];
Where the object is image, and the key is NSNumber key made from index.
I want to sort the array according to the NSNumbers indexes so it will become:
0,1,2,3,4 ..
How can i use NSSortDescriptor ?
The problem (and the debate herein) is complicated by two factors: 1) The OP design choice to sort based on a dictionary key, rather than on a value. #sooper in comments pointed out correctly that the better design would be to add a #"sortBy" key, whose value is the NSNumber to be sorted. 2) The second complication is the question's reference to NSSortDescriptor, which is going to depend upon values for a given key, not the key itself.
I think the right answer is to take the #sooper suggestion to add #"sortBy" key-value pairs, but if you must sort the data as is...
- (void)sortDictionaries {
NSDictionary *d0 = #{ #0: someUIImage0};
NSDictionary *d1 = #{ #1: someUIImage1};
NSDictionary *d2 = #{ #": someUIImage2};
NSArray *unsorted = #[d1, d2, d0];
NSArray *sorted = [unsorted sortedArrayUsingComparator:^(NSDictionary *obj1, NSDictionary *obj2) {
NSNumber *key1 = [self numericKeyIn:obj1];
NSNumber *key2 = [self numericKeyIn:obj2];
return [key1 compare:key2];
}];
NSLog(#"%#", sorted);
}
- (NSNumber *)numericKeyIn:(NSDictionary *)d {
// ps. yuck. what do we want to assume here?
// that it's a dictionary?
// that it has only one key value pair?
// that an NSNumber is always one of the keys?
return [d allKeys][0];
}
Not sure why we had to handle this with so much ill-temperment. It's programming, it's supposed to be fun!
Anyway, here's how you'd do it with a sort key and sort descriptor:
- (void)betterSortDictionaries {
NSDictionary *d0 = #{ #"image":image1, #"sortBy":#0 };
NSDictionary *d1 = #{ #"image":image2, #"sortBy":#1 };
NSDictionary *d2 = #{ #"image":image3, #"sortBy":#2 };
NSArray *unsorted = #[d1, d2, d0];
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"sortBy" ascending:YES];
NSArray *sorted = [unsorted sortedArrayUsingDescriptors:#[descriptor]];
NSLog(#"%#", sorted);
}
I am trying to filter out a NSArray of NSDictionaries. With my below example, I want dict1, dict2 & dict4 grouped in one array, dict3 & dict5 grouped in second array and dict6 in third array.
I am getting this data in NSArray, so essentially the "orig" array below is my input and I know that I need to do grouping based on "Name" key.
Instead of looping through the NSArray I though of using valueForKeyPath to return me array based on the key path but this does not work (crashes with logs -[NSMutableArray addObjectsFromArray:]: array argument is not an NSArray').
Any suggestion.
NSDictionary *dict1 = #{#"Name" : #"T1", #"Age" : #"25"};
NSDictionary *dict2 = #{#"Name" : #"T1", #"Age" : #"25"};
NSDictionary *dict3 = #{#"Name" : #"T2", #"Age" : #"27"};
NSDictionary *dict4 = #{#"Name" : #"T1", #"Age" : #"25"};
NSDictionary *dict5 = #{#"Name" : #"T2", #"Age" : #"27"};
NSDictionary *dict6 = #{#"Name" : #"T3", #"Age" : #"28"};
NSArray *orig = #[dict1, dict2, dict3, dict4, dict5, dict6];
NSMutableArray *final = [NSMutableArray array];
final = [orig valueForKeyPath:#"#unionOfArrays.Name"];
NSLog(#"Final = %#", final);
It's a little hard to tell if what you want is three different arrays where each one only contains entries with a specific Name value (as your first paragraph suggests) or if you want a single array where the entries are sorted by Name (as your second paragraph suggests). Regardless,
To sort orig by the value of the Name field:
NSArray *sortedByName = [orig sortedArrayUsingDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"Name" ascending:YES]]];
To get a new array by selecting only entries with a specific value for Name:
NSArray *t1Only = [orig filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"Name = %#", #"T1"]];
If the desired output is an array of arrays, you can get there by building a dictionary keyed by the name attribute in the orig dictionaries:
- (NSArray *)collateByName:(NSArray *)original {
NSMutableDictionary *collate = [NSMutableDictionary dictionary];
for (NSDictionary *d in original) {
NSString *newKey = d[#"Name"];
NSMutableArray *newValue = collate[newKey];
if (!newValue) {
newValue = [NSMutableArray array];
collate[newKey] = newValue;
}
[newValue addObject:d];
}
return [collate allValues];
}
It's a little verbose, but clear, I think. If you want to decide the attribute to distinguish with programmatically, pass in another param called attribute and replace the literal #"Name" with it.