-
Notifications
You must be signed in to change notification settings - Fork 29.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Array.sort arguments order changed in Node.js 11 #24294
Comments
It's intentional: #22754 (comment) |
Hello @hudochenkov, and thank you for the report. /CC @nodejs/v8 (esp. @targos @hashseed @mathiasbynens) |
P.S. @hudochenkov can you provide some data that using your code sorts differently? |
@hudochenkov stable sort means what if elements in the unsorted array were already in the correct order, they are guaranteed to be in the same order after sorting. in any case, as long as you always return the correct number for two elements, no matter the order, the engine will properly handle it. if the engine switches the order of the arguments, it will also handle the switched return value. |
Thank you for quick replies! I'll simplify my sorting function as much as possible and will provide examples of how it sorts differently. I'll do it tomorrow. |
This comment has been minimized.
This comment has been minimized.
simpler code: const declarations = [
{p: 'mottob', id: 1},
{p: 'bottom', id: 2},
{p: 'mottob', id: 3},
{p: 'mottob', id: 4},
];
if (process.argv[2] === 'anti' ) {
declarations.sort((b, a) => {
const ret = sortDeclarations(a, b);
console.log(`a: %o, b: %o - %d`, a, b, ret);
return -ret
});
} else {
declarations.sort((a, b) => {
const ret = sortDeclarations(a, b);
console.log(`a: %o, b: %o - %d`, a, b, ret);
return ret
});
}
console.log(declarations);
function sortDeclarations(a, b) {
if (b.p === 'bottom') {
return 1;
}
return a.id - b.id;
} Get me: D:\code\prws>d:\bin\dev\node\node10.9.0.exe t.js
a: { p: 'mottob', id: 1 }, b: { p: 'bottom', id: 2 } - 1
a: { p: 'mottob', id: 1 }, b: { p: 'mottob', id: 3 } - -2
a: { p: 'mottob', id: 3 }, b: { p: 'mottob', id: 4 } - -1
[ { p: 'bottom', id: 2 },
{ p: 'mottob', id: 1 },
{ p: 'mottob', id: 3 },
{ p: 'mottob', id: 4 } ]
D:\code\prws>d:\bin\dev\node\node11.0.0.exe t.js
a: { p: 'bottom', id: 2 }, b: { p: 'mottob', id: 1 } - 1
a: { p: 'mottob', id: 3 }, b: { p: 'bottom', id: 2 } - 1
a: { p: 'mottob', id: 4 }, b: { p: 'mottob', id: 3 } - 1
[ { p: 'mottob', id: 1 },
{ p: 'bottom', id: 2 },
{ p: 'mottob', id: 3 },
{ p: 'mottob', id: 4 } ]
D:\code\prws>d:\bin\dev\node\node11.0.0.exe t.js anti
a: { p: 'mottob', id: 1 }, b: { p: 'bottom', id: 2 } - 1
a: { p: 'bottom', id: 2 }, b: { p: 'mottob', id: 3 } - -1
a: { p: 'mottob', id: 1 }, b: { p: 'mottob', id: 3 } - -2
a: { p: 'mottob', id: 1 }, b: { p: 'mottob', id: 4 } - -3
a: { p: 'mottob', id: 3 }, b: { p: 'mottob', id: 4 } - -1
[ { p: 'bottom', id: 2 },
{ p: 'mottob', id: 1 },
{ p: 'mottob', id: 3 },
{ p: 'mottob', id: 4 } ] |
This is not a regression; it’s an implementation detail that shouldn’t be relied upon. |
My above comment was in response to the gist in the OP, i.e. https://gist.github.com/hudochenkov/29b739f8dbdb4aa00a46b953f62dd0a6. @refack, let me respond to your test case now. This program demonstrates something separate from the issue OP reported. The sort callback in this code example is what the spec calls an inconsistent comparison function. For example, assume the following values:
Now, given the way
The result of a sort with an inconsistent comparison function is implementation-defined, and cannot be relied upon. In other words, this too is WAI. |
Thank you for investigation, @refack! And thank you for clarification, @mathiasbynens!
I guess I was just lucky with my sorting function all these years :) I'm going to rewrite it to be independent of arguments order. |
I agree on that. But we still have a user-land regression, due to a dependency on unspecified behaviour. If timsort can call the the compare function in an anti-commutative way, and that will get some percentage of such code to work again, while not breaking the spec, IMO it's a win-win. |
I'm a bit ambivalent on this, but I'm still marking this P.S. in #22754 (comment) I did not object to landing this, even though there was indication that user code was going to break (at least 300 lines in ~50 npm packages) due to using a comparison function that returns |
I don’t see how these situations are different. They’re both examples of problems caused by inconsistent comparison functions. It’s not Node.js’s responsibility to provide support for something that’s explicitly unspecified behavior, IMHO. A comparison function that returns a boolean |
True. Anyway I think we should do a publicity push to get users aware of this situation, and how they should solve issue with inconsistent comparison functions. |
I don’t understand what you mean by this suggestion. |
instead of: const result: Number = sortCompare(context, userCmpFn, x, y); do const result: Number = -sortCompare(context, userCmpFn, y, x); |
AFAICT, that just shifts the problem to other inconsistent comparison functions. |
Imo it's a slippery slope if we started to cater to bugs in user code. Relevant xkcd: https://xkcd.com/1172/ |
To avoid surprises such as nodejs/node#24294
If I do that, then in Firefox browser it will be with the wrong order, because it uses the normal parameters order. Take this example to get the admins at the beginning of the list:
It will return order them by these IDs in Chrome |
@JJBocanegra That's because your comparison function is inconsistent. It returns (a, b) => {
if (a.isAdmin == b.isAdmin) {
return a.id - b.id;
}
if (a.isAdmin) {
return -1;
}
return 1;
} |
@devsnek But that's implying that I want to order by ID, but I do not, I want the initial order to be respected. Consider this:
I want the final order to be
And with your suggestion it would fail too. |
@JJBocanegra Can you open a new issue at https://github.com/nodejs/help/issues/ instead of discussing this one? |
@addaleax sure, but isn't it better to have it here because is directly related to the issue? |
@JJBocanegra As @devsnek said, this is a bug in your code. It’s not really related to this issue here. |
@addaleax I don't think it's a bug in my code, what If I want to use the initial order as a secondary sorting like is the case? With the arguments inverted is not possible without modifying the initial data adding some kind of index because other browsers use the correct arguments order. |
@JJBocanegra you could |
the workaround for handlebars-lang/handlebars.js/issues/748 employed a sorting routine that was not commutative; its output relied on the order of its arguments, and with the V8 engine upgrade in node11, that order has changed[1] the order of the arguments to Array#sory is considered an implementation detail that we can't rely on this patch reimplements that sorting routine to always return the same result regardless of the order of its operands, and so it should work on node <= 10 and >= 11 test plan: there is already a case for this, so I just added node12 to the travis language matrix [1]: nodejs/node#24294
the workaround for handlebars-lang/handlebars.js/issues/748 employed a sorting routine that was not commutative; its output relied on the order of its arguments, and with the V8 engine upgrade in node11, that order has changed[1] the order of the arguments to Array#sory is considered an implementation detail that we can't rely on this patch reimplements that sorting routine to always return the same result regardless of the order of its operands, and so it should work on node <= 10 and >= 11 test plan: there is already a case for this, so I just added node12 to the travis language matrix [1]: nodejs/node#24294
the workaround for handlebars-lang/handlebars.js/issues/748 employed a sorting routine that was not commutative; its output relied on the order of its arguments, and with the V8 engine upgrade in node11, that order has changed[1] the order of the arguments to Array#sory is considered an implementation detail that we can't rely on this patch reimplements that sorting routine to always return the same result regardless of the order of its operands, and so it should work on node <= 10 and >= 11 test plan: there is already a case for this, so I just added node12 to the travis language matrix [1]: nodejs/node#24294
Hi!
I've noticed a change with
Array.sort()
. In Node.js 11 order of arguments passed to compare function is reversed. While in most cases it's not a problem, in some cases it causes problems.In my project I have quite large compare function for
.sort()
: https://github.com/hudochenkov/postcss-sorting/blob/944da947a628192b54448368c197f586fbbe0c10/lib/sorting.js#L10-L62. It relies on arguments order. It works in Node.js 4—10. I don't expect anyone to figure out what my function does, and I can't simplify it for this issue report yet.I created a gist, which shows how arguments get to compare function in Node.js 10 and Node.js 11.
Is it a regression or intentional change?
I tried to understand how V8 7.0 changed
.sort()
, but it's to complex for me :(The text was updated successfully, but these errors were encountered: