Sort Array of Dictionaries by NSDate as inner key - ios

I want to sort the following array by dates. I tried several methods but didn't work. Here the date string is an inner key of the dictionary.
Array is like this
{
message = {
"created_at" = "2015-07-23T07:18:42.315Z";
"created_at_time_ago" = 3ds;
direction = sent;
};
},
{
message = {
"created_at" = "2015-07-21T02:58:23.461Z";
"created_at_time_ago" = 5ds;
direction = sent;
};
},
{
message = {
"created_at" = "2015-07-19T13:32:22.111Z";
"created_at_time_ago" = 7ds;
direction = sent;
};
},
I tries this code, but didn't work
#define KLongDateFormat #"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZ"
NSDateFormatter *fmtDate = [[NSDateFormatter alloc] init];
[fmtDate setDateFormat:KLongDateFormat];
NSComparator compareDates = ^(id string1, id string2)
{
NSDate *date1 = [fmtDate dateFromString:string1];
NSDate *date2 = [fmtDate dateFromString:string2];
return [date1 compare:date2];
};
NSSortDescriptor * sortDesc1 = [[NSSortDescriptor alloc] initWithKey:#"created_at" ascending:NO comparator:compareDates];
[arrSavedFeeds sortUsingDescriptors:#[sortDesc1]];
Why isn't this working?

You can't sort an array of dictionary of dictionary by inner key using NSSortDescriptor. Why don't you just use sortUsingComparator(_:) method of NSMutableArray and compare dictionaries instead?
NSComparator compareDates = ^(id dict1, id dict2)
{
NSDate *date1 = [fmtDate dateFromString:[[dict1 valueForKey:#"message"] valueForKey:#"created"]];
NSDate *date2 = [fmtDate dateFromString:[[dict2 valueForKey:#"message"] valueForKey:#"created"]];
return [date1 compare:date2];
};
[arrSavedFeeds sortUsingComparator:compareDates];

That looks like you are using the ISO 8601 in which case you can save yourself some computation as it can be sorted lexicographically (for positive years).
In which case you can use the following sort descriptor
[NSSortDescriptor sortDescriptorWithKey:#"message.created_at" ascending:NO]

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.

How to sort date?

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

Sorting NSMutableArray based on the content of its objects

I have an NSMutableArray called self.objectArray, that contains custom objects. Each object holds an NSDictionary and two other string objects. Actually I need to work only with the dictionary. Every dictionary contains a key named keyDate which holds an NSString that look like this: MM/dd/yy HH:mm:ss.
I would like to sort the array based on their keyDate. The object with the oldest date should be the first object and so on. I've found some questions, that looked helpful and I could create the code that you can see below, but I get an error everytime I run it. As I think NSSortDescriptor won't be the right tool since my keys aren't key value compliant.
PNMessage 0x125c0590> valueForUndefinedKey:]: this class is not key value coding-compliant for the key keyDate.'
NSSortDescriptor *dateDescriptor = [NSSortDescriptor
sortDescriptorWithKey:#"keyDate"
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:dateDescriptor];
NSArray *sortedEventArray = [self.objectArray
sortedArrayUsingDescriptors:sortDescriptors];
self.finallySorted = [sortedEventArray mutableCopy];
If it's possible I would do it with sort descriptor, however I think there should be some other options, but can't figure out its proper implementation.
So I can also catch every object's keyDate with a for loop, but don't know how can I sort them based on the value. I would really appreciate if somebody could show me the right way.
for(PNMessage *mg in self.objectArray)
{
NSLog(#" test log %#", mg.message[#"keyDate"]);
}
I already checked this answer:
How to sort an NSMutableArray with custom objects in it?
but the structure of my object is different.
My first code based on this question, but it doesn't worked.
How to sort an NSMutableArray with custom objects in it?
UPDATE: my try based on Kaan's answer (doesn't works yet)
static NSDateFormatter *formatter = nil;
if(!formatter) {
formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"MM/dd/yy HH:mm:ss"];
}
NSArray *sortedArray = [self.object sortedArrayUsingComparator:^NSComparisonResult(PNMessage *obj1, PNMessage *obj2) {
NSString *date1String = obj1.message[#"keyDate"];
NSString *date2String = obj1.message[#"keyDate"];
NSDate *date1 = [formatter dateFromString:date1String];
NSDate *date2 = [formatter dateFromString:date2String];
if ( date1 < date2 ) {
return (NSComparisonResult)NSOrderedAscending;
} else if ( date1 > date2 ) {
return (NSComparisonResult)NSOrderedDescending;
}
return (NSComparisonResult)NSOrderedSame;
}];
I would consider using the sortedArrayUsingComparator method
Assuming your custom class is called PNMessage:
static NSDateFormatter *formatter = nil;
if(!formatter) {
formatter = [[NSDateFormatter alloc] init];
[formatter setFormat:#"MM/dd/yy HH:mm:ss"];
}
NSArray *sortedArray = [self.objectArray sortedArrayUsingComparator:^NSComparisonResult(PNMessage *obj1, PNMessage *obj2) {
NSString *date1String = obj1[#"keyDate"];
NSString *date2String = obj1[#"keyDate"];
NSDate *date1 = [formatter dateFromString:date1String];
NSDate *date2 = [formatter dateFromString:date2String];
return [date1 compare:date2];
}];
Tip: If you decide on following this, make sure you declare your NSDateFormatter instance as static outside of the sorting body, since allocating Formatters in iOS can be very expensive and cause serious performance penalties.

display two arrays on uitableview sorted by date

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

EventKit - multiple methods named 'location'

I am trying to list out the Name, Location, and Notes from calendar events. Reading and writing the Name and Notes work as expected but I run into problems with the Location field.
Specifically, the following line "meetingLocation = [element location];" produces the error
"Multiple methods named 'location' found with mismatched result, parameter type or attributes."
What is wrong here? Code is included below.
-(IBAction)reloadEvents:(id)sender {
NSString *meetingName;
NSString *meetingLocation;
NSString *meetingNotes;
// Define a range of event dates we want to display
NSDate *startDate = [NSDate dateWithTimeIntervalSinceNow:(-1*60*60*.5)]; // .5 hour in the past
NSDate *endDate = [NSDate dateWithTimeIntervalSinceNow:(60*60*24*1)]; // 1 day from now
//NSDate *endDate = [NSDate dateWithTimeIntervalSinceNow:(60*60*24*7)]; // 7 days from now
// Create a predicate to search all celndars with our date range using the start date/time of the event
NSPredicate *predicate = [self.eventStore predicateForEventsWithStartDate:startDate endDate:endDate calendars:nil];
// Query the event store using the predicate.
NSArray *results = [self.eventStore eventsMatchingPredicate:predicate];
// Convert the results to a mutable array and store so we can implement swipe to delete
//NSMutableArray *events = [[NSMutableArray alloc] initWithArray:results];
//self.events = events;
NSEnumerator * enumerator = [results objectEnumerator];
id element;
while(element = [enumerator nextObject])
{
// Set the meeting name
meetingName = [element title];
NSLog(#"Name=%#",meetingName);
// Set the meeting location
meetingLocation = [element location];
NSLog(#"Location=%#",meetingLocation);
// Set the meeting notes
meetingNotes = [element notes];
NSLog(#"Notes=%#",meetingNotes);
}
}
Try like this
while(element = [enumerator nextObject])
{
EKEvent *event = element;
meetingName = event.location;
}
Similar problem converting some old iOS 5 to iOS7:
if ([[thisEvent.recurrenceRules objectAtIndex:i] frequency] == EKRecurrenceFrequencyDaily ){
resulted in
Multiple methods names "frequency" found with mismatched result.
Resolved by typecasting and then doing the if statement
EKRecurrenceRule *thisRecurranceRule = (EKRecurrenceRule *)[thisEvent.recurrenceRules objectAtIndex:i] ;
if ([thisRecurranceRule frequency] == EKRecurrenceFrequencyDaily ){

Resources