This adds lexical-sort (naturally sorted) methods to the Finders & Fetch Requests.
More often than not I find that I need the findAllSortedBy/fetchrequest to be sorted lexically (for example "Item 1", "Item 2", …, "Item 10" instead of "Item 1", "Item 10", "Item 2",…) which is the current default.
So as not to break compatibility, I've added Lexical versions to all the findAllSortedBy: methods, which I've named findAllSortedLexicallyBy:.
Perhaps it might be useful to others too.
Added Lexical Sort methods
Certainly a good addition but I am curious how efficient is custom NSSortDescriptor with selector? I believe standard descriptors can be transformed into SQL statements and run on database engine level.
@pronebird We've been using something similar in our current project.
We modified the MR_fetchAll methods in NSManagedObject+MagicalFinders. Instead of passing NSString *sortTerms, we passed an array of sort descriptors (obviously not as easy to make that clear in the .h file that it needs to be an array of NSSortDescriptors, but that would be a precondition). This might have actually been included in a previous release of Magical Record?
In any case, if anyone thinks it's a good idea, I was going to whip up a pull request that allows you to submit an NSArray of NSSortDescriptors or NSStrings so you could pass:
[NSSortDescriptor sortDescriptorWithKey:@"stringKeyPath" ascending:YES selector:@selector(localizedStandardCompare:)];
I can't vouch for the performance, but we deal with some fairly picky clients when it comes to performance, and it hasn't been a problem for us (however, I'm sure there are some users of MagicalRecord out there that deal with much, much bigger data sets). If you turn on SQLite debugging while using localizedStandardCompare: you can see that it's actually used in the query. I think Apple added this private functionality to SQLite.
What's important about this approach is that it allows you to specify any selector you'd like for comparison, localizedStandardCompare just happens to be the one Apple uses for Finder (I think...). If someone came along with a better natural sort algorithm, then you could use that instead of apple's.
@KevinAtJoor totally understand regarding picky clients.
I guess you're right, I just went through the documentation again and it seems Apple supports localizedCompare family of sort descriptors.
I would vote for your PR to allow passing NSArray of NSSortDescriptors as it would be better more flexible solution.
Sorry got mixed up with another email thread!
I'll whip up a PR some time this week. I'll be adding a new method:
Let me know if there's a better way to make it more explicitly obvious that the array needs to be filled with NSSortDescriptors, NSStrings or a comma separated NSString.
@KevinAtJoor NSArray of NSSortDescriptors is good enough.
Thanks for the PR! I had a couple of questions/comments:
Have you tested this with the different store types? (SQLite, Binary, XML, Custom) I had a recollection that certain sorting methods don't work properly with the SQLite-based stores.
I'm not sure that I have a better name for it, but "lexical" doesn't make the API easy to understand for someone new to MagicalRecord. Given that we're not maintaining any kind of existing naming standard for this, perhaps something a bit simpler would be better?
I'd also really like to see a test or two to accompany the new methods you've added here.
@KevinAtJoor passing in custom selectors does sound like a good idea although I'm not sure how frequently might one use custom selectors. I find localizedStandardCompare is the method most often used by myself and others I know, since it handles natural sorting nicely and also handles sorting non-English strings with accents and diacritics properly according to that language's rules; and you're right, it is the method Apple uses for Finder. Maybe if we left the sortDescriptors to be optional, and the users passes in nil to that, the method will by default use localizedStandardCompare. That way most users don't have to worry about which NSSortDescriptor to use, and people with specific needs can specify their custom sort methods.
@tonyarnold yeah, I agree, lexical is not a very friendly term :) sortNaturallyBy definitely sounds a lot better. I've not tested it with non SQLite based stores though. I can test and whip up tests up for these.
"The supported sort selectors are compare: and caseInsensitiveCompare:, localizedCompare:, localizedCaseInsensitiveCompare:, and localizedStandardCompare: (the latter is Finder-like sorting, and what most people should use most of the time)."
The way I see it, there are two levels of flexibility. The first level of flexibility is being able to use all of the sort selectors Apple provides for free in a SQL store. Unfortunately you can't use your own custom selector if this is a SQL backed store. So that's a bit strict but I'd find all of these selectors useful (especially if they were exposed to the user to say, sort case sensitive, insensitive, and naturally).
Second layer of flexibility is using the sort descriptors for the other types of stores. You don't have the same restrictions that the SQL store has, so it's entirely feasible that a client might use custom sort selectors in a non-sql backed store. ("In the XML, binary, and in-memory stores, evaluation of the predicate and sort descriptors is performed in Objective-C with access to all Cocoa's functionality, including the comparison methods on NSString.")
Thirdly, what about tie breakers? The user may want to specify an additional sort descriptor like a number or a date to tie break in case two rows have "A 1" for the string that is being sorted using standardizedLocalCompare.