I have an NSArray which contains custom objects like this:
NSArray *array = {
students,students
}
And student object in turns store values like :
student.name,
student.class,
student.admissionDate
student.school ..etc
Now I want an NSArray which contains all the student's detail sorted in based on their admissionDate.
I tried using NSSortDecriptor but it doesn't helped me.
EDIT
After some hard work I have successfully formed a NSMutable array of NSDictionary which Looks like:
for(Student *arr in array)
{
NSMutableDictionary *dict;
dict = [[NSMutableDictionary alloc]init];
[dict setObject:arr.stuName forKey:#"name"];
[dict setObject:arr.stuAdate forKey:#"date"];
[dict setObject:arr.stuClass forKey:#"class"];
[expenseArray addObject:dict];
}
Printing description of expenseArray:
<__NSArrayM 0x7fe703833f70>(
{
name = uuu;
Adate = "2015-10-10 10:56:03 +0000";
class = 1st;
},
{
name = abc;
Adate = "2015-10-07 11:10:00 +0000";
class = 3rd;
},
{
name = btw;
Adate = "2015-10-10 11:13:47 +0000";
class = 4th;
}
)
Now how can i sort based on date
The sort descriptor should work for you
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"admissionDate" ascending:YES];
NSArray *sortedStudents = [studentArray sortedArrayUsingDescriptors:#[sortDescriptor]];
Below is the working example
NSMutableArray *studentArray = [NSMutableArray new];
for (int i = 0; i < 8; i++) {
Student *student = [Student new];
unsigned int randomInterval = arc4random();
student.admissionDate = [NSDate dateWithTimeIntervalSinceNow:randomInterval];
[studentArray addObject:student];
}
for (Student *student in studentArray) {
NSLog(#"%#", student.admissionDate);
}
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:#"admissionDate" ascending:YES];
NSArray *sortedStudents = [studentArray sortedArrayUsingDescriptors:#[sortDescriptor]];
NSLog(#"\n\n * * * * * After Sorting * * * * * *\n\n");
for (Student *student in sortedStudents) {
NSLog(#"%#", student.admissionDate);
}
Logs
2024-02-11 23:47:47 +0000
2093-11-13 21:49:48 +0000
2042-04-06 23:53:28 +0000
2032-12-23 01:49:46 +0000
2102-12-28 23:08:06 +0000
2058-10-17 14:14:27 +0000
2142-02-01 07:19:34 +0000
2048-05-14 07:07:04 +0000
* * * * * After Sorting * * * * * *
2024-02-11 23:47:47 +0000
2032-12-23 01:49:46 +0000
2042-04-06 23:53:28 +0000
2048-05-14 07:07:04 +0000
2058-10-17 14:14:27 +0000
2093-11-13 21:49:48 +0000
2102-12-28 23:08:06 +0000
2142-02-01 07:19:34 +0000
A sort descriptor should work fine
NSSortDescriptor *admissionSort = [NSSortDescriptor sortDescriptorWithKey:#"Adate" ascending:NO];
NSArray *sortedArray = [array sortedArrayUsingDescriptors:#[admissionSort]];
will sort the array based on the admission date assuming that is actually a NSDate value.
Although NSSortDescription seems to be the best way to sort an array, here is alternative approach:
NSArray *studentsArray = #[someStudent1, someStudent2];
NSArray *sortedStudentsArray = [studentsArray sortedArrayUsingComparator:^NSComparisonResult(Student *student1, Student *student2) {
return [student1.admissionDate compare:student2.admissionDate];
}];
NSSortDescriptor works fine!
You should replace your mutable dictionary with Student class.
#interface Student : NSObject
#property NSString *name;
#property NSString *className;
#property NSString *schoolName;
#property NSDate *admissionDate;
#end
#implementation Student
#end
Uses of this class
Student *student1 = [[Student alloc] init];
student1.name = #"Name a";
student1.className = #"4th";
student1.schoolName = #"XYZ High school";
student1.admissionDate = [NSDate dateWithTimeIntervalSinceNow:60 * 60 * 24]; // 60 * 60 * 24 one day time interval in second
Student *student2 = [[Student alloc] init];
student2.name = #"Name b";
student2.className = #"5th";
student2.schoolName = #"XYZ High school";
student2.admissionDate = [NSDate date]; // current date
NSArray *array = [NSArray arrayWithObjects:student1,student2, nil];
NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:#"admissionDate" ascending:YES];
NSArray *sortedArray = [array sortedArrayUsingDescriptors:#[descriptor]];
for (Student *student in sortedArray) {
NSLog(#"admissionDate %#", student.admissionDate);
}
Please keep in mind that [array sortedArrayUsingDescriptors:#[descriptor]] return sorted array, not rearranged array elements itself.
Hope this will work.
Related
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.
Hi i am new for ios and i am integrating my app with services and i am getting here trip creation TimeandDate values and storing that values one separate Array and also i am getting pickUpAdress values from services and storing them one separate arrayList
After storing in arrayList i am set date values at "Ascending" order in my tableList
after set "Ascending" when i display them in tableList both array-list data they are mismatching to one another please help me how can i resolve this problem how can i set my Adrress array values for matching to related DateandTimes
code:-
#import "ViewController.h"
#interface ViewController ()
{
NSSortDescriptor* sortOrder;
NSArray * ToBeReadyTimeArray1;
NSMutableArray * ToBeReadyTimeArray ,*finalToBeReadyTimeArray,*AdressArray;
UITableView * MaintableView;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
ToBeReadyTimeArray1 = [[NSArray alloc]init];
ToBeReadyTimeArray = [[NSMutableArray alloc]initWithObjects:#"02/17/2016",#"03/13/2016",#"04/18/2016",#"05/21/2016", nil];
finalToBeReadyTimeArray =[[NSMutableArray alloc]init];
AdressArray = [[NSMutableArray alloc]initWithObjects:#"Adress1",#"Adress2",#"Adress3",#"Adress4", nil];
//Arange Date formate At Assending order:-
sortOrder = [NSSortDescriptor sortDescriptorWithKey: #"self" ascending: YES];
ToBeReadyTimeArray1 = [ToBeReadyTimeArray sortedArrayUsingDescriptors: [NSArray arrayWithObject: sortOrder]];
for (NSString *dte in ToBeReadyTimeArray1){
NSDateFormatter *aDateFormatter = [[NSDateFormatter alloc] init];
[aDateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
[aDateFormatter setDateFormat:#"MM/dd/yyyy"];
NSDate * parsed = [aDateFormatter dateFromString:dte];
[aDateFormatter setDateFormat:#"dd MMM yyyy"];
NSString *aFormattedElapsedTime = [aDateFormatter stringFromDate:parsed];
NSLog(#"tobe Ready time %#",aFormattedElapsedTime);
[finalToBeReadyTimeArray addObject:aFormattedElapsedTime];
}
}
I presume you are trying to list pick up times / address with some sorted order. If that's so, i would have created a NSArray of Booking or Trip models which would contain the information as Address (NSString *) and PickUpTime (NSDate *) and then would have implemented a sorting method using the property as variable key.
In my case, I implemented an array of booking models which has pickUpTime and address.
#property (nonatomic, retain) NSNumber *pickUpTime; // Timestamp instead of NSDate ...
#property (nonatomic, retain) NSString *address;
I implemented a category method for NSArray. Please note that sort order enumerations are manually implemented.
+ (NSArray *)sortArray:(NSArray *)unSortedArray byOrder:(SortOrder)sortOrder usingVariableKey:(NSString *)variableKey {
NSArray *sortedArray = [NSArray array];
BOOL isAscendingOrder = NO;
if (sortOrder == SortOrderAscending) {
isAscendingOrder = YES;
}
if (unSortedArray) {
NSSortDescriptor *valueDescriptor = [[NSSortDescriptor alloc] initWithKey:variableKey ascending:isAscendingOrder];
NSArray *descriptors = [NSArray arrayWithObject:valueDescriptor];
sortedArray = [[unSortedArray sortedArrayUsingDescriptors:descriptors] mutableCopy];
}
return sortedArray;
}
Then, i invoked the following ...
// Here, resultMutableArray is a mutable array of booking models ...
NSArray *resultArray = [NSArray sortArray:[resultMutableArray copy] byOrder:SortOrderDescending usingVariableKey:#"pickUpTime"];
i have array Like :
<__NSArrayM 0x79f3a4a0>(
{
"act_date" = "02/03/2015";
"act_id" = 3;
"act_name" = test2;
},
{
"act_date" = "03/03/2015";
"act_id" = 4;
"act_name" = test3;
},
{
"act_date" = "01/03/2015";
"act_id" = 5;
"act_name" = test1;
}
)
i want to sort array with date.
i tried this method :
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"MM/dd/yyyy"];
NSMutableArray *tempArray = [NSMutableArray array];
for (NSString *dateString in tblList)
{
NSDate *date = [formatter dateFromString:[dateString valueForKey:#"act_date"]];
[tempArray addObject:date];
}
[tempArray sortUsingComparator:^NSComparisonResult(NSDate *date1, NSDate *date2) {
// return date2 compare date1 for descending. Or reverse the call for ascending.
return [date2 compare:date1];
}];
But it returns only array with date, how do I add my all data with sorted date array??
I would do this, if you need to convert the values of "act_date" to an proper NSDate before comparison:
NSDateFormatter *_dateFormatter = [[NSDateFormatter alloc] init];
[_dateFormatter setDateFormat:#"MM/dd/yyyy"];
NSArray *_yourOriginalArray = // ...
NSArray *_sortedArray = [_yourOriginalArray sortedArrayUsingComparator:^NSComparisonResult(NSDictionary * obj1, NSDictionary * obj2) {
return [[_dateFormatter dateFromString:[obj2 valueForKey:#"act_date"]] compare:[_dateFormatter dateFromString:[obj1 valueForKey:#"act_date"]]];
}];
then the _sortedArray will have the items sorted.
You are taking out Date from your array, it is not need, you can still sort array of dictionary by using method like below.
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"act_date" ascending:YES];
NSArray *sortedArray = [tblList sortedArrayUsingDescriptors:#[sortDescriptor]];
You can use a simple NSSortDescriptor
NSSortDescriptor * sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"act_date" ascending:YES selector:#selector(compare:)];
array = [array sortedArrayUsingDescriptors:#[sortDescriptor]];
If you want you can add more descriptor to sort for instance by act_id. The priority is set by the oreder of sort descriptors in the array that you pass to the method -sortUsingDescriptor
[EDIT]
As pointed out by holex act_date is a string and it should be converted to NSdate by using NSDateFormatter. Or you will just sort strings.
I am sorting an array of dictionaries with date-
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"act_date" ascending:NO];
[mArrTimeline sortUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
Try this code. May be useful to you.
NSString *strKey = #"act_date";
NSSortDescriptor *sortByName = [NSSortDescriptor sortDescriptorWithKey:strKey ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortByName];
tempArr = (NSMutableArray *)[arr sortedArrayUsingDescriptors:sortDescriptors];
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];
}
I have two arrays one of which is a tweets array and contains twitter tweets. Another of which is a instapics array and contains Instagram pictures. Both of these Arrays have different keys for date. The twitter one had created_at and the instagram one has created_time. I want to display both of them on a single UITableView and have them organized by date. Here is what I am doing so far, the problem with this however is that it only shows Instagram Pictures:
- (void)sortArrayBasedOndate {
NSDateFormatter *fmtDate = [[NSDateFormatter alloc] init];
[fmtDate setDateFormat:#"yyyy-MM-dd"];
NSDateFormatter *fmtTime = [[NSDateFormatter alloc] init];
[fmtTime setDateFormat:#"HH:mm"];
totalFeed = [totalFeed sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
// Instagram Date Retrieval
NSDictionary *instagram = self.instaPics;
NSString *createdAt = instagram[#"created_time"];
int createdAtN = [createdAt intValue];
NSTimeInterval timestamp = (NSTimeInterval)createdAtN;
NSDate *date1 = [NSDate dateWithTimeIntervalSince1970:timestamp];
// Twitter Date Retrieval
NSDictionary *twitter = self.tweets;
NSString *twitterCreated = twitter[#"created_at"];
int createdAtTwitter = [twitterCreated intValue];
NSTimeInterval timestampTwitter = (NSTimeInterval)createdAtTwitter;
NSDate *date2 = [NSDate dateWithTimeIntervalSince1970:timestampTwitter];
return [date1 compare:date2];
}];
}
The above is how I am trying to organize them on the array, below is how I am attempting to display them:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
id object = [totalFeed objectAtIndex:indexPath.row];
if ([tweets containsObject:object]) {
static NSString *Twitter = #"TweetCell";
UITableViewCell *twitter = [self.tableView dequeueReusableCellWithIdentifier:Twitter];
NSDictionary *totalArray = totalFeed[indexPath.row/2];;
// Creation Code Not shown
return twitter;
}else{
static NSString *Instagram = #"InstagramCell";
UITableViewCell *instagram = [self.tableView dequeueReusableCellWithIdentifier:Instagram];
NSDictionary *entry = instaPics[indexPath.row / 2];
// Creation Code not Shown
return instagram;
}
}
The easiest solution would be if you add a common "creationDate" key to all objects in the
totalFeed array. The value of this key should be an NSDate created from
the "created_at" or "created_time" key.
Then you can just sort the array according to this key:
NSSortDescriptor *sortDesc = [[NSSortDescriptor alloc] initWithKey:#"creationDate" ascending:YES]];
totalFeed = [totalFeed sortedArrayUsingDescriptors:#[sortDesc]];
If you cannot do that for some reason, you have to fix the comparator method, it does
not use the passed arguments obj1, obj2 at all.
Something like (pseudo-code):
totalFeed = [totalFeed sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
NSDate *date1, *date2;
if ("obj1 is a Twitter") {
date1 = "get created_at from twitter obj1"
} else {
date1 = "get created_time from instagram obj1"
}
if ("obj2 is a Twitter") {
date2 = "get created_at from twitter obj2"
} else {
date2 = "get created_time from instagram obj2"
}
return [date1 compare:date2];
}];
In any case, in cellForRowAtIndexPath you have to access totalFeed[indexPath.row]
(without dividing by 2, which does not make sense here).
More sample code:
NSArray *instaPics; // your instagrams
NSArray *tweets; // your tweets
NSMutableArray *totalFeed = [NSMutableArray array]; // the common array
// Date formatter for the tweets. The date format must exactly
// match the format used in the tweets.
NSDateFormatter *fmtDate = [[NSDateFormatter alloc] init];
[fmtDate setDateFormat:#"..."];
// Add all instagrams:
for (NSMutableDictionary *instagram in instaPics) {
NSString *createdAt = instagram[#"created_time"];
NSDate *date = [NSDate dateWithTimeIntervalSince1970:[createdAt doubleValue]];
instagram[#"creationDate"] = date;
[totalFeed addObject:instagram];
}
// Add all tweets:
for (NSMutableDictionary *twitter in tweets) {
NSString *twitterCreated = twitter[#"created_at"];
NSDate *date = [fmtDate dateFromString:twitterCreated];
twitter[#"creationDate"] = date;
[totalFeed addObject:twitter];
}
// Sort
NSSortDescriptor *sortDesc = [[NSSortDescriptor alloc] initWithKey:#"creationDate" ascending:YES];
[totalFeed sortUsingDescriptors:#[sortDesc]];