-
Notifications
You must be signed in to change notification settings - Fork 27.4k
fix(orderBy): changes orderBy to use case sensitive comparison to break ... #8112
Conversation
Thanks for the PR! Please check the items below to help us merge this faster. See the contributing docs for more information.
If you need to make changes to your pull request, you can update the commit with Thanks again for your help! |
Hi @mary-poppins I tried to use the issue template, but it wanted to How else can I get an issue logged for this, happy to do anything short of handing complete control of all my code :) |
Don't worry, the issue template isn't absolutely necessary. |
@Narretz Good call, it was naive to do the I pushed a change to optimize that, and benchmarked it against the previous code. For an array of 100,000 objects, each with a name property of 10 random characters, the code on master branch took an average of 1586ms, the code in this PR took an average of 1545ms. So I think performance is now on par with master, and shouldn't be a problem. |
return v1 < v2 ? -1 : 1; | ||
} | ||
v1 = v1Lower; | ||
v2 = v2Lower; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is duplicate code... Why not do:
var v1Lower = v1.toLowerCase();
var v2Lower = v2.toLowerCase();
if (v1Lower !== v2Lower) {
v1 = v1Lower;
v2 = v2Lower;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works for me!
@shahata pushed your suggested changes. |
@Narretz @mary-poppins @shahata anything else I can do to help get this merged? |
If you want to get this merged, a good start would be fixing the lint errors which are causing Travis to fail. You can test those locally with However, I'm not big on this change --- particularly, it's very strange to have the default behaviour be case insensitive in some ways, and case sensitive in other ways. The orderBy filter allows you to provide a custom comparator, so any kind of case-sensitivity you want to provide is easily pluggable, so I'm not really sure what this is really adding, other than making the behaviour inconsistent. Maybe its' a "good" inconsistency that people would be happy with, but maybe it's not, I'm not sure. |
…ak ties on strings Previously running orderBy on an array of strings such as ['A cow', 'a cow'] would result in ['A cow', 'a cow'], regardless of the value of reverseOrder. This is because orderBy cast all strings to lowercase before comparing them. This commit changes the behavior to result in ['a cow', 'A cow'] when reverseOrder is true, and ['A cow', 'a cow'] when reverse order is false.
Thanks @caitp, pushed fix for the lint error.
I don't think it is possible to provide a custom comparator, only a custom 'getter' function. The comparator is always the built in (case-insensitive) one. As I noted above this change doesn't introduce any performance penalty, and the benefit is correctness and consistency. On master currently var array = [{name: "John"}, {name: "john"}];
var asc = orderBy(array, 'name', true);
var desc = orderBy(array, 'name', false);
expect(asc).toEqualData(desc.reverse());
// Error: Expected [ { name : 'John' }, { name : 'john' } ] to equal data [ { name : 'john' }, { name : 'John' } ]. As you can see, regardless of the value of the reverseOrder parameter, the return value of For what it's worth I have actual users in an actual app who are complaining to me that reversing the sort order of a list does not exactly reverse the list. It's because of them that I am on this quest to make the |
The predicate parameter. |
Actually, I might be wrong about that, lets find out! |
yeah, we only have a custom comparison operator for the filterFilter, not orderBy. That's probably the right way to implement this, IMO. But an alternative predicate notation could be used too, maybe. |
@caitp I'm not sure accepting a custom comparator is necessary because unlike filtering, ordering has a commonly understood definition. Angular does well with that as the common expectation, it gets the ascending alphabetical order correct out of the box. Where things go off the rails IMHO is the common expectation that there is some sort of total order, such that sorting a list in descending order is the "opposite", or reverse, of sorting a list in ascending order; that the order of elements in a sorted list depends only on their identity and their relationship to other elements in the list, not to the order they were inserted in the (original, unsorted) list and not to their identity after being secretly run through some other function. Angular fails in that expectation, and this PR is a remedy. A very small and performance neutral remedy. |
It's not that simple, actually. The predicate array does a bit to help customize the way these comparisons work, but custom comparator functions would actually do this a lot better --- we'd be easier on the callstack, we'd be simpler to implement. I think we should see what Igor thinks about this one, because this is a pretty minor change, but I'm not certain it's necessarily the "right" change. |
@IgorMinar can we get your thoughts here? |
02dc2aa
to
fd2d6c0
Compare
cad9560
to
f294244
Compare
e8dc429
to
e83fab9
Compare
4dd5a20
to
998c61c
Compare
With this change the 3rd argument is interpreted as a comparator function, used to compare the values returned by the predicates. Leaving it empty falls back to the default comparator and a value of `false` is a special case that also falls back to the default comparator but reverses the order. Thus the new implementation is backwards compatible. Helps with angular#12572 (maybe this is as close as we want to get). Fixes angular#13238 Fixes angular#14455 Closes angular#5123 Closes angular#8112 Closes angular#10368
With this change the 3rd argument is interpreted as a comparator function, used to compare the values returned by the predicates. Leaving it empty falls back to the default comparator and a value of `false` is a special case that also falls back to the default comparator but reverses the order. Thus the new implementation is backwards compatible. This commit also xpands the documentation to cover the algorithm used to sort elements and adds a few more unit and e2e tests (unrelated to the change). Helps with angular#12572 (maybe this is as close as we want to get). Fixes angular#13238 Fixes angular#14455 Closes angular#5123 Closes angular#8112 Closes angular#10368
With this change the 3rd argument is interpreted as a comparator function, used to compare the values returned by the predicates. Leaving it empty falls back to the default comparator and a value of `false` is a special case that also falls back to the default comparator but reverses the order. Thus the new implementation is backwards compatible. This commit also expands the documentation to cover the algorithm used to sort elements and adds a few more unit and e2e tests (unrelated to the change). Helps with angular#12572 (maybe this is as close as we want to get). Fixes angular#13238 Fixes angular#14455 Closes angular#5123 Closes angular#8112 Closes angular#10368
Add an optional, 4th argument (`comparator`) for specifying a custom comparator function, used to compare the values returned by the predicates. Omitting the argument, falls back to the default, built-in comparator. The 3rd argument (`reverse`) can still be used for controlling the sorting order (i.e. ascending/descending). Additionally, the documentation has been expanded to cover the algorithm used by the built-in comparator and a few more unit and e2e tests (unrelated to the change) have been added. Helps with #12572 (maybe this is as close as we want to get). Fixes #13238 Fixes #14455 Closes #5123 Closes #8112 Closes #10368 Closes #14468
...ties on strings
Previously running orderBy on an array of strings such as ['A cow', 'a cow'] would
result in ['A cow', 'a cow'], regardless of the value of reverseOrder. This is because orderBy cast
all strings to lowercase before comparing them.
This commit changes the behavior to result in ['a cow', 'A cow'] when reverseOrder is true,
and ['A cow', 'a cow'] when reverse order is false.