Sorting NSMutableArray based on the content of its objects - ios

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.

Related

IOS/Objective-C: Build NSDictionary from NSManagedObject

I am trying to build an NSDictionary from an NSManagedObject. I was under the impression that you could do this with: NSMutableDictionary *myDict;
[myDict setObject:self.title forKey:#"title"];
}
Because a dictionary cannot hold nil values, I am testing for nil first. However, when I verify using a break that properties have values, when I build the dictionary it is nil. It shows as nil using a breakpoint and logs to console as NULL. Would appreciate someone confirming that the following is a valid way to create a dictionary and, if so, why the dictionary would be nil?
NSString *title = #"Test";
NSNumber *complete = #1;
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat = #"yyyy-MM-dd HH:mm:ss";
NSString *lasttouchedstr =[dateFormatter stringFromDate:[NSDate date]];
NSMutableDictionary *myDict;
if (self.title.length>=1) {
[myDict setObject:self.title forKey:#"title"];
}
if (![self.complete isKindOfClass:[NSNull class]]) {
[myDict setObject:self.complete forKey:#"complete"];
}
if (![self.lasttouched isKindOfClass:[NSNull class]]) {
[myDict setObject:lasttouchedstr forKey:#"lasttouchedstr"];
}
NSLog(#"myDict is:%#",myDict)//Logs as NULL
return myDict;
Thanks in advance for any suggestions.
NSMutableDictionary *myDict; declares the value but does not initialize it. You need
NSMutableDictionary *myDict = [[NSMutableDictionary alloc] init];
Secondly, managed objects will not be returning a value of NSNull for any of their attributes. You need to check for != nil instead. See here for a nice explanation of the differences between nulls and nils.

Sorting NSMutableArray that contains NSMutableDictionaries [duplicate]

This question already has answers here:
How do I sort an NSMutableArray with custom objects in it?
(27 answers)
Closed 7 years ago.
I'm trying to figure out the best/most efficient way to sort an array that contains n-number of dictionaries. One of the key/value pairs in each dictionary is a date field. After adding all the dictionaries to the array, I would like to sort the array by descending date order.
For example, I have code like this:
NSMutableArray *myArray = [[NSMutableArray alloc] init];
NSMutableDictionary *dictionary1 = [[NSMutableDictionary alloc] init];
NSDate *today = [NSDate date];
[dictionary1 setObject:today forKey:#"date"];
[dictionary1 setObject:#"Another value" forKey:#"anotherKey"];
[myArray addObject:dictionary1];
NSMutableDictionary *dictionary2 = [[NSMutableDictionary alloc] init];
NSDate *tomorrow = [[NSDate date] dateByAddingTimeInterval:60*60*24];
[dictionary2 setObject:tomorrow forKey:#"date"];
[dictionary2 setObject:#"Yet another value" forKey:#"anotherKey"];
[myArray addObject:dictionary2];
Now I need myArray to be sorted by descending date. (array index 0 should be the latest date)
Note: In my actual project, I'm not creating and adding the dictionaries in this way. But for example purposes to see how the date is stored in the dictionary, lets assume I've put these two into the array.
You can use NSSortDescriptors here:
NSMutableArray *myArray = [[NSMutableArray alloc] init];
NSMutableDictionary *dictionary1 = [[NSMutableDictionary alloc] init];
NSDate *today = [NSDate date];
[dictionary1 setObject:today forKey:#"date"];
[dictionary1 setObject:#"Another value" forKey:#"anotherKey"];
[myArray addObject:dictionary1];
NSMutableDictionary *dictionary2 = [[NSMutableDictionary alloc] init];
NSDate *tomorrow = [[NSDate date] dateByAddingTimeInterval:60*60*24];
[dictionary2 setObject:tomorrow forKey:#"date"];
[dictionary2 setObject:#"Yet another value" forKey:#"anotherKey"];
[myArray addObject:dictionary2];
NSSortDescriptor *sortDesciptor = [NSSortDescriptor sortDescriptorWithKey:#"date" ascending:NO];
//Create new sorted array
NSArray *sortedArray = [myArray sortedArrayUsingDescriptors:#[sortDesciptor]];
//Or sort your mutable one
[myArray sortUsingDescriptors:#[sortDesciptor]];
There are lots of ways to do this. You could use an NSSortDescriptor as Krivoblotsky says.
You can also use the NSMutableArray sortUsingComparator method. The code would look something like this:
[myArray sortUsingComparator
^(NSDictionary *obj1, NSDictionary *obj2)
{
return [obj1["date"] compare: obj2["date"]]
}
];
The sortUsingComparator method takes an NSComparator block.
An NSComparator takes two objects of type id, and returns an NSComparisionResult:
typedef NSComparisonResult (^NSComparator)(id obj1, id obj2);
Since NSDate supports the compare method you can just write 1-line comparator block that fetches the date entry for each dictionary and returns the result of comparing them.

Sorting NSMutableArray with date in ios7

hi i am new in IOS i have one array name with array1. Each index of an array there is one dictionary with 4 different fields like name,date,marks,standard. these all values are in NSString format. i want to sort this array1 by date. so can any one help me to do this please
here below is some part of my code
NSMutableDictionary *tempDict = [[NSMutableDictionary alloc]init];
NSString *myname = name.text;
NSString *marks= marks.text;
NSString *date=date.text;
NSString *address=address.text;
NSString *rID=rID.text;
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyMMdd"];
NSDate *date1 = [dateFormat dateFromString:date];
[tempDict setObject:myname forKey:#"name"];
[tempDict setObject:marks forKey:#"marks"];
[tempDict setObject:date1 forKey:#"date"];
[tempDict setObject:address forKey:#"address"];
[tempDict setObject:rID forKey:#"Rid"];
[array1 addObject:tempDict];
NSSortDescriptor *sortByDate = [NSSortDescriptor sortDescriptorWithKey:#"date"
ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortByDate];
NSArray *sortedArray = [array1 sortedArrayUsingDescriptors:sortDescriptors];
If you want to sort your array with the date then you have to create NSDate object instead of NSString
NSDate *date = [dateFormatter dateFromString:date.text];
where dateFormatter is your NSDateFormatter
I think -sortUsingComparator: is the cleanest option.
[array1 sortUsingComparator: (id obj1, id obj2) {
return [[obj1 objectFoKey: #"date"] compare: [obj2 objectForKey: #"date"]];
}];
I dont know about the size of your array but if it is not something very big, you can do this:
make a dictionary (we call it sortingDict) with number of index (in array1) as key and your date of the corresponding array object as the value. You should iterate through your array1 to do this of course. (it would be better if you create NSDate objects of your string dates by the way).
get sortingDict all values. ([sortingDict allValues]) We call this dateArray.
sort your date array using NSSortDescriptor or any other algorithm you may want to use. We call it sortedDateArray.
now in a for loop iterating through sortedDateArray you get the keys of your sortingDict in order (You can use [sortingDict allKeysForObject:] and put it in an array sortedIndices
now you have an array with indices of array1 in your desired sorted order.
reordering your initial array1 wouldn't be a problem I believe.
P.S: This is a very inefficient way that just got out of the top of my head, hope it helps.
you can do like this.. if you wanted to display in tableview than other arrays are useful.
if(array1 > 0)
{
NSMutableArray *UniqueDates = [array1 valueForKeyPath:#"#distinctUnionOfObjects.date"];
[recordDates addObjectsFromArray:UniqueDates];
NSArray *cleanedArray = [[NSSet setWithArray:recordDates] allObjects];
recordDates = [cleanedArray mutableCopy];
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"self" ascending:NO];
[recordDates sortUsingDescriptors:[NSArray arrayWithObjects:descriptor, nil]];
[mainarray addObjectsFromArray:array1];
}
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"date == %#", [recordDates objectAtIndex:YourIndex]];
// yourindex == indexpath.row if you are displaying in table view
NSArray *filterContacts= [array1 filteredArrayUsingPredicate:predicate];
mainarray = [filterContacts mutableCopy];
recordDates and mainarray is also mutablearray.

NSArray contains nothing after method call

I'm calling a method passing two arrays to it as follow :
NSArray *marked_dates = [NSArray arrayWithObjects:[dateFormat dateFromString:#"19/03/2014"],[dateFormat dateFromString:#"17/04/2014"],[dateFormat dateFromString:#"12/02/2014"], nil];
NSArray *marked_colors = [NSArray arrayWithObjects:[UIColor greenColor],[UIColor greenColor],[UIColor greenColor],nil];
[calendar markDates:marked_dates withColors:marked_colors];
Below is the method receiving this :
-(void)markDates:(NSArray *)dates withColors:(NSArray *)colors {
self.markedDates = dates;
self.markedColors = colors;
NSLog(#"%#",[NSString stringWithFormat:#"%d",[dates count]]);
for (int i = 0; i<[self.markedDates count]; i++) {
NSLog(#"%#",[NSString stringWithFormat:#"%#", self.markedDates[i]]);
}
[self setNeedsDisplay];
}
The log says 0 and I obviously don't get into the for loop.
Thanks for any help.
EDIT :
I will also vote up for any information about the warning : "format string is not a string literal"
Below the declaration of the properties :
#property (nonatomic, retain) NSArray *markedDates;
#property (nonatomic, retain) NSArray *markedColors;
EDIT :
So yes, the formater gives null values.
Here is how I did it :
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"MM/dd/yyyy"];
NSLog(#"%#",[NSString stringWithFormat:#"%#",[dateFormat dateFromString:#"19/03/2014"]]);
//this prints "null"
EDIT with solution :
Bad mistake, I misconfigured the formatter
using this worked for me, thank you all for your help.
[dateFormat setDateFormat:#"dd/MM/yyyy"];
The dates array is probably empty due to the date formatter only producing nil values. Fix the date formatter so it will correctly generate NSDate objects and it should work:
[dateFormat setDateFormat:#"dd/MM/yyyy"];
First of all you should not create the property name and Local instance variable as same name. Because property it's self create instance var with _Property name.And the issue is with line.
NSArray *marked_dates = [NSArray arrayWithObjects:[dateFormat dateFromString:#"19/03/2014"],[dateFormat dateFromString:#"17/04/2014"],[dateFormat dateFromString:#"12/02/2014"], nil];
Add the date format also.
check this again. let me know if you still facing the same issue.
[dateFormat setDateFormat:#"dd/MM/yyyy"]; ?

Sorting the dates in an array?

In my application I have an array of strings representing the dates in the Format MM/dd/YYYY
and below is the code I used to sort this array
NSDateFormatter *formatter =[[NSDateFormatter alloc]init];
[formatter setDateFormat:#"MM/dd/YYYY"];
NSLog(#" arr %#",arr);
[arr sortUsingComparator:^NSComparisonResult(id obj1, id obj2)
{
NSDate *date1=[formatter dateFromString:obj1];
NSDate *date2=[formatter dateFromString:obj2];
return [date1 compare:date2];
}];
NSLog(#" arr %#",arr);
below is the output of nslog
2013-04-08 17:23:48.112 SEEMR[2792:c07] arr (
"02/18/2013",
"02/16/2013",
"02/14/2013",
"01/16/2013",
"02/13/2013",
"03/16/2013"
)
2013-04-08 17:24:07.662 SEEMR[2792:c07] arr (
"02/18/2013",
"02/16/2013",
"02/14/2013",
"01/16/2013",
"02/13/2013",
"03/16/2013"
)
But it is not sorting as expected so help me peers
When using NSDateFormatter, always test if your formatting string is correct. Yours isn't. It should be MM/dd/yyyy (the case is important).
In other words, since your formatter works incorrectly, all dateFromString: return nil and you are always comparing nil with a nil, which returns 0.
Saving your dates as a NSDate instances would make your life easier. In general, it's better to convert the date into NSString only if you are presenting it to the user or sending it to some other application (e.g. web service).
You can do something like this:
NSSortDescriptor* sortByDate = [NSSortDescriptor sortDescriptorWithKey:#"date property name" ascending:YES];
[mutableArray sortUsingDescriptors:[NSArray arrayWithObject:sortByDate]];
Hope it helps!

Resources