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.
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.
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];
I am having an array which contains a date. this is my array.
"2015-03-01",
"2015-03-04",
"2015-03-05",
"2015-03-06",
"2015-03-07",
"2015-03-08",
"2015-03-14",
"2015-03-15"
list the value according to this date.my coding is
NSArray * datevalue = [tempArr valueForKey:#"match_formatted_date"];
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"self" ascending:YES];
NSArray *descriptors = [NSArray arrayWithObject: descriptor];
NSArray *reverseOrder = [datevalue sortedArrayUsingDescriptors:descriptors];
NSPredicate *findFutureDates = [NSPredicate predicateWithBlock: ^BOOL(id obj, NSDictionary *bind){
NSDateFormatter *df = [[NSDateFormatter alloc]init];
[df setDateFormat:#"yyyy-MM-dd"];
[df setTimeZone:[NSTimeZone systemTimeZone]];
NSDate *dd = [df dateFromString:(NSString *)obj ];
return ([[NSDate date] compare:dd] == NSOrderedAscending);
}];
NSArray * arrFutureDates = [reverseOrder filteredArrayUsingPredicate: findFutureDates];
NSLog(#"arrFutureDates:%#",arrFutureDates);
for (id item in arrFutureDates)
{
if (![dataArr containsObject:item])
[dataArr addObject:item];
}
[dataArr sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
for (int i= 0; i<[dataArr count]-1; i++) {
for (id item in tempArr)
{
if([#"Mexico: Copa Mexico - Clausura" isEqualToString:[item valueForKey:#"league_name"]]
||[#"Mexico: Liga De Ascenso - Clausura" isEqualToString:[item valueForKey:#"league_name"]]
||[#"Mexico: Primera Division - Clausura" isEqualToString:[item valueForKey:#"league_name"]])
if([[dataArr objectAtIndex:i]isEqualToString:[item valueForKey:#"match_formatted_date"]])
{
NSMutableDictionary *tempDic =[[NSMutableDictionary alloc]init];
[tempDic setValue: [item valueForKey:#"match_formatted_date"] forKey:#"match_formatted_date"];
[tempDic setValue: [item valueForKey:#"league_name"] forKey:#"league_name"];
if (![titleheader containsObject:tempDic])
{
[titleheader addObject:tempDic];
}}}}
my problem is: In title header null value is coming. were I made the mistake, can anyone help me.
Check your code, you didnt initialize titleheader anywhere. In that case it will be nil only.
Before checking condition, initialize and assign some value to titleheader.
Or else i dont know you missed to paste the code.
Unreadable formatting aside, there are few issues in your code, that may cause your problem:
NSArray* datevalue = [tempArr valueForKey:#"match_formatted_date"];
Name suggests, that tempArr is NSArray, however you are accessing it as NSDictionary. There is difference between methods -valueForKey: and -objectForKey:. Former is NSObject method related to KVC, while latter is NSDictionary method for accessing stored objects. They are frequently confused, because called on NSDictionary they behave in the same way.
for (int i= 0; i<[dataArr count]-1; i++) {
This for loop skips last object in dataArr, which - I assume - was not intended. This can be fixed by i < dataArr.count or i <= dataArr.count-1.
Another issue - not related to problem, but opportunity to learn:
NSMutableDictionary *tempDic =[[NSMutableDictionary alloc]init];
[tempDic setValue: [item valueForKey:#"match_formatted_date"] forKey:#"match_formatted_date"];
[tempDic setValue: [item valueForKey:#"league_name"] forKey:#"league_name"];
if (![titleheader containsObject:tempDic])
Objects in containers are stored and compared (by default) as references, thus -containsObject will always return NO - even if there were another NSDictionary with the same key-value pairs.
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.
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.