I have an NSMutableDictionary that maps NSString to NSString (although the values are NSStrings, they are really just integers).
For example consider the following mappings,
"dog" --> "4"
"cat" --> "3"
"turtle" --> "6"
I'd like to end up with the top 10 entries in the dictionary sorted by decreasing order of the value. Can someone show me code for this? Perhaps there is an array of keys and another array of values. However it is, I don't mind. I'm just trying to have it be efficient.
Thank you!
Get the Array of the Values, sort that array and then get the key corresponding to the value.
You can get the values with:
NSArray* values = [myDict allValues];
NSArray* sortedValues = [values sortedArrayUsingSelector:#selector(comparator)];
But, if the collection is as you show in your example, (I mean, you can infer the value from the key), you can always sort the keys instead messing with the values.
Using:
NSArray* sortedKeys = [myDict keysSortedByValueUsingSelector:#selector(comparator)];
The comparator is a message selector which is sent to the object you want to order.
If you want to order strings, then you should use a NSString comparator.
The NSString comparators are i.e.: caseInsensitiveCompare or localizedCaseInsensitiveCompare:.
If none of these are valid for you, you can call your own comparator function
[values sortedArrayUsingFunction:comparatorFunction context:nil]
Being comparatorFunction (from AppleDocumentation)
NSInteger intSort(id num1, id num2, void *context)
{
int v1 = [num1 intValue];
int v2 = [num2 intValue];
if (v1 < v2)
return NSOrderedAscending;
else if (v1 > v2)
return NSOrderedDescending;
else
return NSOrderedSame;
}
The simplest way is:
NSArray *sortedValues = [[yourDictionary allValues] sortedArrayUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
NSMutableDictionary *orderedDictionary=[[NSMutableDictionary alloc]init];
for(NSString *valor in sortedValues){
for(NSString *clave in [yourDictionary allKeys]){
if ([valor isEqualToString:[yourDictionary valueForKey:clave]]) {
[orderedDictionary setValue:valor forKey:clave];
}
}
}
Use this method:
- (NSArray *)sortKeysByIntValue:(NSDictionary *)dictionary {
NSArray *sortedKeys = [dictionary keysSortedByValueUsingComparator:^NSComparisonResult(id obj1, id obj2) {
int v1 = [obj1 intValue];
int v2 = [obj2 intValue];
if (v1 < v2)
return NSOrderedAscending;
else if (v1 > v2)
return NSOrderedDescending;
else
return NSOrderedSame;
}];
return sortedKeys;
}
Call it and then create a new dictionary with keys sorted by value:
NSDictionary *dictionary = [[NSDictionary alloc] initWithObjectsAndKeys:
#"4", #"dog",
#"3", #"cat",
#"6", #"turtle",
nil];
NSArray *sortedKeys = [self sortKeysByIntValue:dictionary];
NSMutableDictionary *sortedDictionary = [[NSMutableDictionary alloc] init];
for (NSString *key in sortedKeys){
[sortedDictionary setObject:dictionary[key] forKey:key];
}
Sorting the keys and using that to populate an array with the values:
NSArray *keys = [dict allKeys];
NSArray *sKeys = [keys sortedArrayUsingSelector:#selector(caseInsensitiveCompare:)];
NSMutableArray *sValues = [[[NSMutableArray alloc] init] autorelease];
for(id k in sKeys) {
id val = [dict objectForKey:k];
[sValues addObject:val];
}
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"interest" ascending:YES];
[unsortedArray sortUsingDescriptors:[NSArray arrayWithObjects:descriptor,nil]];
recentSortedArray = [stories copy];
if you want to sort data in ascending order for key 'name' for such kind of Example then this may help you.
arrayAnimalList = [
{
'name' = Dog,
'animal_id' = 001
},
{
'name' = Rat,
'animal_id' = 002
},
{
'name' = Cat,
'animal_id' = 003
}
];
This is a code which help you to get sorted array
//here you have to pass key for which you want to sort data
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"name" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:descriptor];
// here you will get sorted array in 'sortedArray'
NSMutableArray * sortedArray = [[arrayAnimalList sortedArrayUsingDescriptors:sortDescriptors] mutableCopy];
Related
This question already has answers here:
How to do a natural sort on an NSArray?
(5 answers)
Closed 6 years ago.
How to sort an NSMutableArray numerically when it contains objects like follows:
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:#"P3",#"P1",#"P4",#"P10", nil];
The output of the same should be like: P1, P3, P4, P10
You need to use NSNumericSearch
[array sortUsingComparator:^NSComparisonResult(NSString* _Nonnull str1, NSString* _Nonnull str2) {
return [str1 compare:str2 options:NSNumericSearch];
}];
From the Header Documentation-
NSNumericSearch = 64, /* Added in 10.2; Numbers within strings are compared using numeric value, that is, Foo2.txt < Foo7.txt < Foo25.txt; only applies to compare methods, not find */
Hope this is what you are looking for.
Assuming there is always a character at the start of the strings, then:
[array sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSInteger i1 = [[obj1 substringFromIndex:1] integerValue];
NSInteger i2 = [[obj2 substringFromIndex:1] integerValue];
if (i1 > i2)
return NSOrderedDescending;
else if (i1 < i2)
return NSOrderedAscending;
return NSOrderedSame;
}];
try this code:
NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:#"P3",#"P1",#"P4",#"P10", nil];
NSLog(#"Array: %#",array);
NSSortDescriptor *lastNameDescriptor = [[NSSortDescriptor alloc] initWithKey:nil ascending:YES selector:#selector(localizedStandardCompare:)];
NSArray *sorters = [[NSArray alloc] initWithObjects:lastNameDescriptor, nil];
NSArray *sortedArray = [array sortedArrayUsingDescriptors:sorters];
NSMutableArray *sortArray = [[NSMutableArray alloc] init];
[sortArray addObjectsFromArray:sortedArray];
NSLog(#"sortArray : %#",sortArray);
Output::
2016-06-15 16:47:47.707 test[5283:150858] Array: (
P3,
P1,
P4,
P10
)
2016-06-15 16:47:47.708 test[5283:150858] sortArray : (
P1,
P3,
P4,
P10
)
You can also sort NSArrayor NSMutableArray using NSSortDescriptor.
NSSortDescriptor *sd = [NSSortDescriptor sortDescriptorWithKey:#"self" ascending:YES];
NSArray *sortedArray = [<arrayToBeSorted> sortedArrayUsingDescriptors:#[sd]];
NSLog(#"Result = %#", sortedArray);
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);
}
NSArray A = #[[[#"id":#"3"]], [[#"id":#"4"]] ,[[#"id":#"c"]],[[#"id":#"f"]]];
NSArray idArray = #[#"c", #"3", #"4",#"f"];
Just a example I assumed.
How can I sort A by its id with idArray?
That is, I want A to become:
NSArray A= #[[[#"id":#"c"]], [[#"id":#"3"]] ,[[#"id":#"4"]],[[#"id":#"f"]]];
Now, I want to ask for an algorithm to sort array A to get the desired result.
---I get my answer when I search in google:
NSArray *sorter = #[#"B", #"C", #"E"];
NSMutableArray *sortee = [#[
#[#"B", #"abc"],
#[#"E", #"pqr"],
#[#"C", #"xyz"]
] mutableCopy];
[sortee sortUsingComparator:^(id o1, id o2) {
NSString *s1 = [o1 objectAtIndex:0];
NSString *s2 = [o2 objectAtIndex:0];
NSInteger idx1 = [sorter indexOfObject:s1];
NSInteger idx2 = [sorter indexOfObject:s2];
return idx1 - idx2;
}];
If you want to compare both array you can use
NSArray *array1=#[#"3",#"4",#"c","f"];
NSArray *array2=#[#"c",#"3",#"4","f"];
array1=[array1 sortedArrayUsingSelector:#selector(compare:)];
array2=[array2 sortedArrayUsingSelector:#selector(compare:)];
if ([array1 isEqualToArray:array2]) {
NSLog(#"both are same");
}
else{
NSLog(#"both are differnt");
}
or If you want to get common elements from 2 array use
NSMutableSet* set1 = [NSMutableSet setWithArray:array1];
NSMutableSet* set2 = [NSMutableSet setWithArray:array2];
[set1 intersectSet:set2]; //this will give you only the obejcts that are in both sets
NSArray* result = [set1 allObjects];
This would be a better way to make a dictionary for A. And then sorting based on their specific values like IQ, Name etc.
NSArray A = #[[[#"id":#"3"]], [[#"id":#"4"]] ,[[#"id":#"c"]],[[#"id":#"f"]]];
NSArray idArray = #[#"c", #"3", #"4",#"f"];
NSMutableArray *array = [NSMutableArray array];
for (int id = 0;idx<[A count];id++) {
NSDictionary *dict = #{#"Name": A[id],#"IQ":idArray[id]};
[array addObject:dict];
}
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:#"IQ" ascending:NO];
[array sortUsingDescriptors:#[descriptor]]
i have 2 nsarrays
1 with nsdictionary's another with nsnumbers
NSArray *arr1 = #[#{#"id":#1},#{#"id":#2},#{#"id":#3},#{#"id":#4}];
NSArray *arr2 = #[#3,#1,#4,#2];
and i want to sort my arr1 through their id following the order of arr2
is this possible?
The problem with using sortedArrayUsingComparator: is you start dealing with O(n^2) lookup times. For each sort comparison in the first array, you have to do a lookup in the second array.
Your best bet is to take advantage of a hash table to reduce that to O(n) average complexity.
Your first step is to create a dictionary using id as a key. The result would look something like #{#1: #{#"id":#"1"}, ...}. Then you just have to construct an array by looping through arr3 and grabbing the values.
NSArray *arr1 = #[#{#"id":#1},#{#"id":#2},#{#"id":#3},#{#"id":#4}];
NSArray *arr2 = #[#3,#1,#4,#2];
NSMutableDictionary *map = [NSMutableDictionary dictionary];
for (NSDictionary *item in arr1) {
map[item[#"id"]] = item;
}
NSMutableArray *arr3 = [NSMutableArray array];
for (id key in arr2) {
[arr3 addObject:map[key]];
}
This solution of course assumes parity between the two arrays. If arr2 has an element not in arr1 it will crash when trying to add nil to arr3. If arr1 has a value not in arr2 it will be excluded from arr3. These are risks you will have to address based on your requirements.
Here is how you can do it by using a custom comparator:
NSArray* sorted= [arr1 sortedArrayUsingComparator: ^NSComparisonResult(NSDictionary *obj1, NSDictionary *obj2) {
return [arr2 indexOfObject:obj1[#"id"]] - [arr2 indexOfObject:[obj2[#"id"]];
}];
I exploited the fact that NSComparisonResult has +1 to represent an ascending order, -1 for descending and 0 to represent the same order.
- (NSArray*) sortedArray
{
NSArray *arr1 = #[#{#"id":#1},#{#"id":#2},#{#"id":#3},#{#"id":#4}];
NSArray *arr2 = #[#3,#1,#4,#2];
NSMutableArray *mutableArray = [NSMutableArray new];
for (NSNumber *number in arr2)
{
for (NSDictionary* dictionary in arr1)
{
NSNumber *number2 = dictionary[#"id"];
if ([number isEqual:number2])
{
[mutableArray addObject:dictionary];
break;
}
}
}
return mutableArray;
}
So I have an array of custom "Element" objects (hey hold atomic number, chemical symbol, atomic mass, etc...) and I am having trouble sorting them by one of their properties;
Here is the code:
switch (sortDescriptor) {
case 0: {
//Sort the array by "ATOMIC NUMBER"
NSArray *sortedArray = [self.elementsArray sortedArrayUsingComparator:^(id a, id b) {
NSNumber *first = #([(SAMElement *)a atomicNumber]);
NSNumber *second = #([(SAMElement *)b atomicNumber]);
return [first compare:second];
}];
self.elementsArray = [sortedArray mutableCopy];
}
case 1: {
//Sort the array by "ELEMENT NAME"
NSArray *sortedArray = [self.elementsArray sortedArrayUsingComparator:^(id a, id b) {
NSString *first = [(SAMElement *)a elementName];
NSString *second = [(SAMElement *)b elementName];
return [first compare:second];
}];
self.elementsArray = [sortedArray mutableCopy];
}
case 2:{
NSLog(#"sorting by chemical symbol");
//Sort the array by "CHEMICAL SYMBOL"
NSArray *sortedArray = [self.elementsArray sortedArrayUsingComparator:^(id a, id b) {
NSString *first = [(SAMElement *)a chemichalSymbol];
NSString *second = [(SAMElement *)b chemichalSymbol];
return [first compare:second];
}];
self.elementsArray = [sortedArray mutableCopy];
}
case 3: {
//Sort the array by "ATOMIC MASS"
NSArray *sortedArray = [self.elementsArray sortedArrayUsingComparator:^(id a, id b) {
NSNumber *first = [(SAMElement *)a atomicMass];
NSNumber *second = [(SAMElement *)b atomicMass];
return [first compare:second];
}];
self.elementsArray = [sortedArray mutableCopy];
}
default:
break;
}
When is sorts it returns a totally random list of elements. Am i doing something wrong?
The best way to sort an array of objects by some property of the object, its using NSSortDescriptor. In initWithKey, you can set the name of the property that you want to sort.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"atomicNumber" ascending:NO];
[self.elementsArray sortUsingDescriptors:#[sortDescriptor]];
In your case, just copy this code above in each case section of your switch statement, changing the key for #"elementName" and #"chemichalSymbol".
You can change the ascending value from NO to YES, depending what type of order do you want.
Please, let me know if worked or not.
I'm not seeing the bug immediately, but you're reinventing the wheel here. The correct tool for this is sortedArrayUsingDescriptors:
[self.elementsArray sortedArrayUsingDescriptors:#[
[NSSortDescriptor alloc] initWithKey:#"atomicNumber"] ascending:YES]
]];
Try that and see if it gets rid of your bug. If you're getting random orders, that usually suggests that your comparitor is inconsistent (sometimes A>B and sometimes B>A for the same A&B).