Sort string contains numbers, NSMutableArray with Dictionaries - ios

strings like
"4 Miles 400 stones"
"2 Miles 10 stones"
"6 Miles 2 Stones"
a key value of dictionary in NsMutableArray, I am trying to sort them by the amount of miles then stones.
regular sortUsingDescriptor :
[list sortUsingDescriptors:[NSArray arrayWithObjects:[NSSortDescriptor sortDescriptorWithKey:#"systems" ascending:YES], nil]];
or NSNumericSearch :
NSMutableArray *newList;
NSArray *result = [list sortedArrayUsingFunction:&sort context:#"systems"];
newList= [[NSMutableArray alloc] initWithArray:result];
NSInteger sort(id a, id b, void* p) {
return [[a valueForKey:(__bridge NSString*)p]
compare:[b valueForKey:(__bridge NSString*)p]
options:NSNumericSearch];
}
are not working.
Do I have to parse the string get numbers then sort it? or is there an easier way to sort this?

Here is how to do it the best, object-oriented way.
First. Create a class. Let's call it MyObject:
#interface MyObject : NSObject
#property(nonatomic, assign) NSUInteger miles;
#property(nonatomic, assign) NSUInteger stones;
+ (MyObject *)objectWithString:(NSString *)string;
#end
As you can see, it has a objectWithString that we will use to create objects using information in a string like: "4 Miles 400 stones".
#implementation MyObject
+ (MyObject *)objectWithString:(NSString *)string
{
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:#"[0-9]+?(?= Miles | stones)" options:0 error:nil];
NSArray *matches = [regex matchesInString:string options:0 range:NSMakeRange(0, [string length])];
MyObject *myObject = [[MyObject alloc] init];
myObject.miles = [[string substringWithRange:((NSTextCheckingResult *)matches[0]).range] integerValue];
myObject.stones = [[string substringWithRange:((NSTextCheckingResult *)matches[1]).range] integerValue];
return myObject;
}
- (NSString *)description
{
return [NSString stringWithFormat:#"%d miles, %d stones", self.miles, self.stones];
}
#end
Then, we will use NSSortDescriptor to sort our array:
MyObject *myObject1 = [MyObject objectWithString:#"4 Miles 400 stones"];
MyObject *myObject2 = [MyObject objectWithString:#"2 Miles 10 stones"];
MyObject *myObject3 = [MyObject objectWithString:#"6 Miles 2 stones"];
NSArray *array = #[myObject1, myObject2, myObject3];
NSSortDescriptor *miles = [[NSSortDescriptor alloc] initWithKey:#"miles" ascending:YES];
NSSortDescriptor *stones = [[NSSortDescriptor alloc] initWithKey:#"stones" ascending:YES];
NSArray *sortDescriptors = #[miles, stones];
NSArray *sortedArray = [array sortedArrayUsingDescriptors:sortDescriptors];
NSLog(#"Sorted: %#", sortedArray);
And the output:
2014-03-05 19:51:54.233 demo[12267:70b] Sorted: (
"2 miles, 10 stones",
"4 miles, 400 stones",
"6 miles, 2 stones" )
It works like a charm my friend!

Your method is correct,
I have tried your way of
NSInteger sort(id a, id b, void* p) {
return [[a valueForKey:(__bridge NSString*)p]
compare:[b valueForKey:(__bridge NSString*)p]
options:NSNumericSearch];
}
it is sorting correctly make sure you call correct dictionary value or correct method, put some break points, you may be sending null values or something.
if you want to reverse the search just use
NSInteger reverseSort(id a, id b, void* p) {
return - [[a valueForKey:(__bridge NSString*)p]
compare:[b valueForKey:(__bridge NSString*)p]
options:NSNumericSearch];
}

Related

sort elements in NSArray based on timestamp of element in my case

I have an array of NSString, each NSString element contains a timestamp (epoch time) and other characters, e.g.: "time:1474437948687, <other characters>".
NSArrary *myData = [self loadData];// my array
So, myData looks like this inside:
{"time:1474437948687,fajlsfj...",
"time:1474237943221, axsasdfd...",
"time:1474681430940, someother...",
...
}
I need to have an array which contains the same elements as the above array, but are sorted in descending order of the timestamp. How can I do it?
I get stuck with iterating over the array of NSString:
for (NSString element in myData) {
...
}
Use following sortedArrayUsingComparator it will work for me : I use static data e.g time:1474437948687, ..
**time:1474437948687, <other characters> Consider String Format..**
NSArrary *myData = [self loadData];
NSArrary *sortedmyData = [[myData sortedArrayUsingComparator: ^(id obj1, id obj2) {
NSDateFormatter *df = [[NSDateFormatter alloc] init];
// Change Date formate accordingly ====
[df setDateFormat:#"dd-MM-yyyy"];
NSDate *d1 = [df dateFromString:[self sperateDate:obj1]];
NSDate *d2 = [df dateFromString:[self sperateDate:obj2]];
return [d1 compare: d2];
}];
// This is function is developed as per time:1474437948687, Consider String Format..
- (NSString *)sperateDate : (NSString *)obj1 {
NSArray *arr = [obj1 componentsSeparatedByString:#","];
NSArray *arr1 = [arr[0] componentsSeparatedByString:#":"];
return arr1[1];
}
Update Compare with primitive type :
NSArray *myData = #[#"time:1474437948687,fajlsfj...",#"time:1474237943221, axsasdfd...",#"time:1474681430940, someother..."
NSArray *sortedmyData = [myData sortedArrayUsingComparator: ^(id obj1, id obj2) {
NSNumber *d1 = [NSNumber numberWithDouble:[[self sperateDate:obj1] doubleValue]];
NSNumber *d2 = [NSNumber numberWithDouble:[[self sperateDate:obj2] doubleValue]];
return [d1 compare: d2];
}];
Hope this help you...
Do let me know if you have any other query
Try this :
NSArray *timearray = #[#"time:1474437948687,fajlsfj...",
#"time:1474237943221, axsasdfd...",
#"time:1474681430940, someother..."];
NSSortDescriptor *sortDescriptor;
sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#""
ascending:NO];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray *sortedArray = [timearray sortedArrayUsingDescriptors:sortDescriptors];
NSLog(#"sortedArray %#",sortedArray);
Output:
sortedArray (
"time:1474681430940, someother...",
"time:1474437948687,fajlsfj...",
"time:1474237943221, axsasdfd..."
)
First of all it seems to be a good idea to transform the string into instances of an entity type that reflects the data in a key-value manner. Then you can sort it easily by using an instance of NSSortDescriptor:
NSSortDescriptor *timeSorter = [NSSortDescriptor sortDescriptorWithKey:#"time" ascending:NO];
NSArray *sorted = [myData sortedArrayUsingSortDescriptors:#[timeSorter]];
However, you can sort the array as is by using a more complex sort descriptor:
NSSortDescriptor *timeSorter = [NSSortDescriptor sortDescriptorWithKey:#"self" ascending:NO comparator:
^(id one, id two )
{
NSString *timestamp1 = [one compenentsSepartedByString:#","][0];
timestamp1 = [timestamp1 substringFromIndex:5];
NSString *timestamp2 = [two compenentsSepartedByString:#","][0];
timestamp2 = [timestamp2 substringFromIndex:5];
return [timestamp1 compare:timestamp2 options:NSNumericSearch range:NSMakeRange(0, [timestamp1 length])];
}];
NSArray *sorted = [myData sortedArrayUsingSortDescriptors:#[timeSorter]];
Typed in Safari.

Sorting NSMutableArray Removing First Element [duplicate]

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);

Objective C custom compare: with priority

I have a NSArray of countries that I obtained using [NSLocale ISOCountryCodes]. How do I sort this NSArray such that I can put certain commonly used countries at the top of the list, while keeping the rest in its alphabetical order?
United States of America
United Kingdom
Singapore
Korea
Japan
Hong Kong
Afghanistan
Albania
Algeria
etc etc..
I am using caseInsensitiveCompare: currently to get the alphabetical order, but how can I change it such that I can specify a list to put at the top, while the rest to be kept alphabetical below.
You can delete the objects you do not want to sort,then sort the rest,then add them together.
Example Code:
NSMutableArray * allData = [[NSMutableArray alloc] initWithArray:[[NSLocale ISOCountryCodes]]];
NSArray * yourexceptArray;
[allData removeObjectsInArray:yourexceptArray];
NSMutableArray * result = [[NSMutableArray alloc] initWithArray:yourexcepArray];
sortedArray = //Sort the allData as you like,then add it to result
[result addObjectsFromArray:sortedArray]
As opposed to WenchenHuang's answer (which is valid), I think you could do it with the help of sortedArrayUsingComparator.
Inside the block just compare the strings as usually, but if the string equals to one of the codes that you want to show higher, return YES.
someArray = [someArray sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
if ([(NSString*)obj1 isEqualToString:#"USA"]) {
return YES;
} else if ([(NSString*)obj1 isEqualToString:#"SOME_OTHER_COUNTRY"]) {
return YES;
}
return [(NSString*)obj1 compare:(NSString*)obj2];
}];
I would do it using Sort Descriptor. I don't like manipulating array again and again. So, I find sort descriptors best in this kind of scenario. (Just personal preference)
Step 1: Create a model class with priority as a key. My model class-
#import <Foundation/Foundation.h>
#interface ISOCountry : NSObject
#property(nonatomic, strong) NSString *countryCode;
#property(nonatomic, retain) NSString *priority;
#end
Step 2: The just do this-
NSArray *ISOCountryCodes = [[NSArray alloc]initWithArray:[NSLocale ISOCountryCodes]];
NSArray *commonUsedCountries= [[NSArray alloc]initWithObjects:#"NR", #"NG", #"KW", #"ES", nil];
NSMutableArray *arrayToBeSorted = [[NSMutableArray alloc]init];
for(NSString *countryCode in ISOCountryCodes){
ISOCountry *isoCountry = [[ISOCountry alloc] init];
[isoCountry setValue:countryCode forKey:#"countryCode"];
if(![commonUsedCountries containsObject:countryCode]){
[isoCountry setValue:[NSString stringWithFormat:#"%d", 2] forKey:#"priority"];
}
else{
[isoCountry setValue:[NSString stringWithFormat:#"%d", 1] forKey:#"priority"];
}
[arrayToBeSorted addObject:isoCountry];
}
NSSortDescriptor *prioritySort = [[NSSortDescriptor alloc] initWithKey:#"priority" ascending:YES];
NSSortDescriptor *countryCodeSort = [[NSSortDescriptor alloc] initWithKey:#"countryCode" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObjects:prioritySort, countryCodeSort, nil];
NSArray *sortedArray = [arrayToBeSorted sortedArrayUsingDescriptors:sortDescriptors];

Trouble sorting an array of custom objects

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).

Sort an NSMutableDictionary

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

Resources