I've got an array of dictionaries. Each dictionary has a string object for a key #"date". I need to sort the array by this date, descending. How can I do it the best way?
You can create a NSSortDescriptor, for example:
NSSortDescriptor *sorter = [NSSortDescriptor sortDescriptorWithKey:#"date" ascending:YES];
[array sortUsingDescriptors:#[sorter]];
This will use string comparison to compare your date strings. It should work if the date string is formatted correctly and consistently.
If i understood the structure of this array, I hope this answer helped! :)
I used this method to sort an array that holds an array that held anther array that finally held a string I wanted to sort the top array by. So obviously something simple doesn't work. But i ran into this:
- (void)sortUsingComparator:(NSComparator)cmptr;
In your case, i hope, this is what you'd use:
NSMutableArray *arrayItems = [NSMutableArray arrayWithObjects:
#{#"date":[NSDate date]},
#{#"date":[NSDate dateWithTimeIntervalSinceNow: 60*60*45]},
#{#"date":[NSDate dateWithTimeIntervalSinceNow: 60*60*25]},
#{#"date":[NSDate date]},
#{#"date":[NSDate dateWithTimeIntervalSinceNow: 60*60*5]},
#{#"date":[NSDate dateWithTimeIntervalSinceNow: 60*60*32]},
#{#"date":[NSDate date]},
#{#"date":[NSDate dateWithTimeIntervalSinceNow: 60*60]},
nil];
[arrayItems sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { //obj1 and obj2 are objects in the list of the array. so in your case a list of dictionaries, so obj1 and obj2 are dictionaries, mutable
//And now, which is why you would use this block to sort your objects, there you'll access each date, or object, you want to have the 'power' to sort the objects inside the caller, in this case arrayItems
NSDate *date1 = [obj1 objectForKey: #"date"];
NSDate *date2 = [obj2 objectForKey: #"date"];
//And now you compare the two of whom is larger
return [date1 compare: date2];
}];
NSLog( #"%#", arrayItems);
I hope this helped, or hinted you to the answer :)
The best way will be to format that strings into NSDate objects and use compare: method.
Related
I have an NSMutable array of objects. The objects represent football (soccer) matches and have an NSString parameter called title (ed "Arsenal v Chelsea"), and an NSDate parameter called ActualDate.
I am sorting the array by date at the moment using the following code:
NSMutableArray* a = [self getMatchListFromURL:#"http://www.url.com"];
[a sortUsingDescriptors:#[[NSSortDescriptor sortDescriptorWithKey:#"ActualDate" ascending:YES]]];
Obviously there are multiple games that happen on the same date. I would like to sort games that happen on the same date in alphabetical order. Is there a simple way to do this?
The method sortUsingDescriptors takes array of NSSortDescriptor as an argument. So you can pass multiple sort descriptors to method as follow:
NSSortDescriptor *sortAlphabetical = [NSSortDescriptor sortDescriptorWithKey:#"title" ascending:YES];
NSSortDescriptor *sortByDate = [NSSortDescriptor sortDescriptorWithKey:#"ActualDate" ascending:YES];
NSArray *sortDescriptors = #[sortAlphabetical, sortByDate];
//perform sorting
[a sortUsingDescriptors:sortDescriptors];
There are a few ways to implement this, but I think the most readable is to implement a comparison block, like so:
[a sortUsingComparator:^NSComparisonResult(SomeClass *obj1, SomeClass *obj2) {
NSComparisonResult dateCompare = [obj1.actualDate compare:obj2.actualDate];
if (dateCompare != NSOrderedSame) {
return dateCompare;
} else {
return [obj1.title compare:obj2.title];
}
}];
This will sort a first by its actualDate property, and if they're the same, then by the titleproperty. You can add additional logic if you need to.
You could, alternatively, add additional NSSortDescriptor objects to the array you pass to sortUsingDescriptors:, but I think that's less readable.
I've got the following NSArray :
NSArray myArray = #[#{#300:#"5 min"},
#{#900:#"15 min"},
#{#1800:#"30 min"},
#{#3600:#"1 hour"}];
I want the list of value of my dictionaries :
#[#"5 min",#"15 min",#"30 min",#"1 hour"]
And the list of key of my dictionaries :
#[#300, #900, #1800, #3600]
What is the best way to do that ? I was thinking about predicate, but I don't know how to use it ?
Without some code to show how you'd want to go about this it is difficult to be sure exactly what you are after, and there is a bit of confusion in the question.
First, a predicate is exactly that - a statement that can be proven true or false. Predicates are hence used in logic expressions, including those employed implicitly in database queries - such as Core Data.
That is not what you want, if I read your question correctly. What you want is to reduce the complexity of your data model, removing some excess (one would hope) information in the process. A sort of flattening of an array of dictionaries.
Fair enough.
I can also see how the confusion with predicates came about - they are most often constructed using Key-Value Coding. KVC, as it is also known, is a very powerful technique that can accomplish what you are after. It just does not have much to do with a logic statement.
Having cleared that up, with KVC you can do what you want, and with minimal fuss. It goes like this:
NSArray *values = [myArray valueForKeyPath: #"#unionOfArrays.#allValues"];
NSArray *keys = [myArray valueForKeyPath: #"#unionOfArrays.#allKeys"];
A brief explanation might be in order:
The results that we want are
All the values (or keys) of each dictionary, obtaining an array of arrays of values (or keys)
Then we want to flatten these arrays into a single array.
To obtain all values (or keys) from a dictionary using KVC, the special key is #allValues or #allKeys, respectively.
The #unionOfArrays operator makes a union of the arrays obtained from the expression that follows it, i.e., flattens it into the array you wanted.
The price you pay for this coding simplicity is that you have to use KVC key paths with collection operators, which are just strings in your code. You therefore lose any help from the compiler with syntax and it doesn't check that the keys you enter exist in the objects. Similarly, the debugger and error messages are unhelpful if you mistype or use the wrong operator, for instance.
You can use dictionary property allValues to get all values of dictionary.
Try this code in your case
NSArray *myArray = #[#{#300:#"5 min"},
#{#900:#"15 min"},
#{#1800:#"30 min"},
#{#3600:#"1 hour"}];
NSMutableArray *arr = [NSMutableArray array];
for (NSDictionary *dict in myArray) {
[arr addObject:[[dict allValues] objectAtIndex:0]];
}
NSLog(#"%#",arr);
Note : Make sure you have only one value in each dictionary.
it will return
[
5 min,
15 min,
30 min,
1 hour
]
#johnyu's answers is technically correct, but I don't see any reason to include the secondary loop, especially if the data structure will remain the same.
NSArray *myArray = #[#{#300:#"5 min"},
#{#900:#"15 min"},
#{#1800:#"30 min"},
#{#3600:#"1 hour"}];
NSMutableArray *arrayOfValues = [NSMutableArray new];
NSMutableArray *arrayOfKeys = [NSMutableArray new];
for (NSDictionary *dictionary in myArray) {
[arrayOfValues addObject:dictionary.allValues[0]];
[arrayOfKeys addObject:dictionary.allKeys[0]];
}
NSLog(#"%#",arrayOfKeys);
NSLog(#"%#",arrayOfValues);
Try this:
NSArray *myArray = #[#{#300:#"5 min"},
#{#900:#"15 min"},
#{#1800:#"30 min"},
#{#3600:#"1 hour"}];
NSMutableArray *keyArray = [[NSMutableArray alloc] init];
NSMutableArray *valueArray = [[NSMutableArray alloc] init];
for (NSDictionary *dictionary in myArray) {
for (NSString *key in dictionary) {
[keyArray addObject:key];
[valueArray addObject:[dictionary objectForKey:key]];
}
}
I am sorting an NSMutableArray on my field "date" but it does not take into account the time. If the date is the same, the time is still sorted random. What am I doing wrong? The field "date" is of the type NSDate.
NSSortDescriptor *dateDescriptor =
[[NSSortDescriptor alloc] initWithKey:#"date" ascending:YES];
NSArray *descriptors = [NSArray arrayWithObjects:dateDescriptor, nil];
[self.player1ScoreT sortedArrayUsingDescriptors:descriptors];
My output is:
2012-11-25 11:01:00 +0000
2012-11-25 11:00:56 +0000
2012-11-25 11:00:54 +0000
2012-11-25 11:01:03 +0000
Strange.....
The logic you're using to sort is correct, I've just tried it and it works fine.
I assume it's because of [self.player1ScoreT sortedArrayUsingDescriptors:descriptors];, this will not change the array held in self.player1ScoreT, rather, it produces a new array. This is because you're using a selector that will not mutate the original array (NSArray itself is not mutable by design, NSMutableArray is mutable).
As such, you'll want to use this:
self.player1ScoreT = [self.player1ScoreT sortedArrayUsingDescriptors:descriptors];
This will reassign the array within self.player1ScoreT to be the new sorted array, produced by sortedArrayUsingDescriptors:, and the old unsorted one will be discarded.
As WDUK says, you're missing the assignment of the sortedArray:
id newArray = [oldArray sortedArrayUsing...
I even tried it and it sorts dates ok INCLUDING time
code I tried:
int main(int argc, char *argv[]) {
#autoreleasepool {
id player1ScoreT = #[#{#"date":[NSDate dateWithTimeIntervalSinceNow:-100]},
#{#"date":[NSDate dateWithTimeIntervalSinceNow:-1000]},
#{#"date":[NSDate dateWithTimeIntervalSinceReferenceDate:+1000]},
#{#"date":[NSDate dateWithTimeIntervalSinceReferenceDate:-50]},
#{#"date":[NSDate dateWithTimeIntervalSinceNow:+50]},
#{#"date":[NSDate dateWithTimeIntervalSinceNow:+0]}];
NSLog(#"%#", [player1ScoreT valueForKeyPath:#"date"]);
player1ScoreT = [player1ScoreT sortedArrayUsingDescriptors:#[[[NSSortDescriptor alloc] initWithKey:#"date" ascending:YES]]];
NSLog(#"%#", [player1ScoreT valueForKeyPath:#"date"]);
}
}
I have an NSMutableArray, called categories, which contains an object called CategoryItem. CategoryItem has an NSString property, *text. Now how would I sort this Array based on the text property of the elements? Sorry if this doesn't make sense, I'm new to all this.
I tried this:
[categories sortUsingSelector:#selector(localizedCaseInsensitiveCompare:)];
But it failed.
That's because you're trying to sort the array based on each object, not on the string it contains.
You need to use an instance of NSSortDescriptor to sort your array. You can create one like this:
NSSortDescriptor *descriptor = [[NSSortDescriptor alloc] initWithKey:#"text" ascending:YES];
Then:
[categories sortUsingDescriptors:[NSArray arrayWithObject:descriptor]];
Try this:
[categories sortedArrayUsingComparator:^NSComparisonResult(id obj1, id obj2) {
CategoryItem object1 = (CategoryItem)obj1;
CategoryItem object2 = (CategoryItem)obj2;
return [object1.text compare:object2.text];
}];
Hope that helps!
It fails because you did not implement localizedCaseInsensitiveCompare for CategoryItem.
If you want to do that, implement that function for CategoryItem like
- (NSComparisonResult)localizedCaseInsensitiveCompare:(CategoryItem *)anItem
{
return [anItem.stringProperty localizedCaseInsensitiveCompare:self.stringProperty];
}
You kinda did this right.
The problem being that you're trying to sort strings, but you dont have strings, you have CategoryItems. To keep the same code, you would just implement localizedCaseInsensitiveCompare: for CategoryItem. In that function, compare the text value and return the correct NSComparisonResult. Probably something like this
-(NSComparisonResult)localizedCaseInsensitiveCompare:(CategoryItem *)item2 {
return [text localizedCaseInsensitiveCompare:[item2 text]];
}
I have an NSDictionary which contains 3 NSArrays,
- posts
- comments
- likes.
And in each array are consistent NSObject Subclasses:
- Post
- Comment
- Like
Usually, I would just put all objects from these three arrays into one array and compare them using the same variable which they all contain, but in this case, Posts has the variable dateOfUpload and Like and Comment have the same variable, date.
How can I compare the objects from these three arrays using the variable date and dateOfUpload to create one big array of all objects in descending date?
I would make them all implement method like:
-(NSComparsionResult) compareByDate : (NSObject *) obj;
Of course you'll need to implement this in different way in each class.
Then make one big array from all the tree arrays and call
[myArray sortUsingSelector:#selector(compareByDate:)];
Another way to do this is to add all your objects in one big array and sort using a block as follows:
// Create the array with all the objects
NSMutableArray *stuff = [NSMutableArray arrayWithArray:posts.allValues];
[stuff addObjectsFromArray:comments.allValues];
[stuff addObjectsFromArray:likes.allValues];
// Sort it by using a block
NSArray *sortedStuff = [stuff sortedArrayUsingComparator:^(id obj1, id obj2) {
NSDate *date1 = [obj1 respondsToSelector:#selector(date)]? [obj1 date] : [obj1 dateOfUpload];
NSDate *date2 = [obj2 respondsToSelector:#selector(date)]? [obj2 date] : [obj2 dateOfUpload];
return [date2 compare:date1]; // Objects are reversed to get descending order
}];