-
Notifications
You must be signed in to change notification settings - Fork 27.4k
Update(orderBy): Guaranteed stable sort #15914
Update(orderBy): Guaranteed stable sort #15914
Conversation
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.
A couple of minor comments only.
I am not sure about the BC tbh. I don't really consider it to be one. It would only break usecases where all of the following conditions are true:
- The app is using a custom comparator.
- The custom comparator fails to differentiate between two values.
- The app expects both ascending and descending sorting to yield the same results (which depend on the browser).
test/ng/filter/orderBySpec.js
Outdated
var reversed = ['B', 'a', 'c']; | ||
var sorted = ['a', 'B', 'c']; | ||
|
||
expect(orderBy(items)).toEqual(sorted); |
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 doesn't seem related.
src/ng/filter/orderBy.js
Outdated
@@ -115,7 +115,7 @@ | |||
* | |||
* @param {boolean=} reverse - If `true`, reverse the sorting order. | |||
* @param {(Function)=} comparator - The comparator function used to determine the relative order of | |||
* value pairs. If omitted, the built-in comparator will be used. | |||
* value pairs. If omitted or finds two entries to be equal, the built-in comparator will be used. |
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.
I think this is more confusing than helpful. If they are interested they will read the details below.
Fair enough about the breaking change. I wasn't sure either, but figured it would be better to write it up than gloss over it. I'll get right to work on those changes. |
854c03f
to
8f236af
Compare
@@ -599,7 +602,7 @@ function orderByFilter($parse) { | |||
} | |||
} | |||
|
|||
return compare(v1.tieBreaker, v2.tieBreaker) * descending; | |||
return (compare(v1.tieBreaker, v2.tieBreaker) || defaultCompare(v1.tieBreaker, v2.tieBreaker)) * descending; |
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.
I do not fully understand this change, this should be just
return defaultCompare(v1.tieBreaker, v2.tieBreaker) * descending;
There is no reason for an external function to understand the internal tieBreaker
structure.
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 not intended to defer to the defaultCompare
function over the user-provided function, but rather to provide a reasonable fallback should a user-provided function fail to the break the tie. See #14481 (comment)
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.
@lgalfaso, for better or worse, this tiebreaker structure is documented and has been used with custom comparator 😕 Do you think we should change that?
I thought it is useful to give the custom comparator a chance to break the tie in its own way (in the rare case where this will be needed).
Was not aware that this was added as part of the public API. Once this was
added, then I think it is best to keep it.
…On Fri, Apr 14, 2017 at 1:00 PM George Kalpakas ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In src/ng/filter/orderBy.js
<#15914 (comment)>:
> @@ -599,7 +602,7 @@ function orderByFilter($parse) {
}
}
- return compare(v1.tieBreaker, v2.tieBreaker) * descending;
+ return (compare(v1.tieBreaker, v2.tieBreaker) || defaultCompare(v1.tieBreaker, v2.tieBreaker)) * descending;
@lgalfaso <https://github.com/lgalfaso>, for better or worse, this
tiebreaker structure is documented and has been used with custom comparator
😕 Do you think we should change that?
I thought it is useful to give the custom comparator a chance to break the
tie in its own way (in the rare case where this will be needed).
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#15914 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAX44gelh5eDy1EUF7MJaAOqjkTOLxESks5rv1G-gaJpZM4M8VuB>
.
|
Was there anything else pending or can we go ahead and mark this as ready to be merged? I think we've covered all the concerns that have been raised. |
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.
Can you also change the commit message to adhere to our guidelines. E.g.:
fix(orderByFilter): guarantee stable sort
If the user-provided comparator fails to break a tie, fall back to the built-in comparator.
Fixes #14881
src/ng/filter/orderBy.js
Outdated
@@ -460,6 +460,9 @@ | |||
* way. (When specifying a custom comparator, you also need to pass a value for the `reverse` | |||
* argument - passing `false` retains the default sorting order, i.e. ascending.) | |||
* | |||
* If your comparator fails to differentiate between two items, they will fall back to the built-in | |||
* comparator function, this produces a guarantee of stable sorting. | |||
* |
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.
Actually, this is the wrong place for this comment. It should be moved to L55 and be changed to something like:
If a custom comparator still can't distinguish between two items, then they will be sorted based on their index using the built-in comparator.
If user-provided comparison function fails to differentiate between two elements, fall back to built-in comparator. Fixes angular#14881
8f236af
to
fe5f585
Compare
Apologies about the commit message, apparently it's been a while since I read the contributing guidelines and I use a similar scheme on another project. |
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.
A couple of nit. I will fix up while merging.
@@ -49,9 +49,8 @@ | |||
* second, or `1` otherwise. | |||
* | |||
* In order to ensure that the sorting will be deterministic across platforms, if none of the | |||
* specified predicates can distinguish between two items, `orderBy` will automatically introduce a | |||
* dummy predicate that returns the item's index as `value`. | |||
* (If you are using a custom comparator, make sure it can handle this predicate as well.) |
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 line should not be removed. It is still necessary for the comparator to handle the tie-break value. E.g. if it has an array of strings, it should not expect that all values will be strings, because there is the possibility of getting passed a number (the index).
* specified predicates can distinguish between two items, `orderBy` will automatically introduce a | ||
* dummy predicate that returns the item's index as `value`. | ||
* (If you are using a custom comparator, make sure it can handle this predicate as well.) | ||
* specified predicates nor the custom comparator can distinguish between two items, `orderBy` will |
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 change is confusing imo. The custom comparator is not something additional to the predicates. They are used together (the predicates provide the values and the comparator compares them).
What kind of change does this PR introduce? (Bug fix, feature, docs update, ...)
Feature
What is the current behavior? (You can also link to an open issue here)
#14881
What is the new behavior (if this is a feature change)?
Falls back on built-in comparison function if user-provided comparison function fails to break a tie between elements being ordered.
Does this PR introduce a breaking change?
Yes
Please check if the PR fulfills these requirements
Other information:
BREAKING CHANGE: Using
orderBy
with a customcomparator
function whilesimultaneously setting
reverse
totrue
results in a potentially differentlist order than previously.
Specifically, when the custom
comparator
fails to sort two objects (comparesthem as being "equal"), the objects would previously have remained in their
original order in the array relative to each other. They will now switch places
as the
reverse
will be more accurately respected.To migrate, make sure that your custom
comparator
properly differentiatesbetween all cases that your code base cares about and that "equal" cases are
not order dependent. We expect that due to the nature of this change, since the
entries are considered "equal", that very few projects will be affected by this
change in all but a minor visual manor that still respects the expected
operation being performed.