Skip to content
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

fix comparator bisector #250

Merged
merged 2 commits into from Apr 11, 2022
Merged

fix comparator bisector #250

merged 2 commits into from Apr 11, 2022

Conversation

mbostock
Copy link
Member

@mbostock mbostock commented Apr 9, 2022

Fixes #249. Previously #227.

If the bisector was given an asymmetric comparator that takes (object, value) as arguments, then the test for whether the search value is orderable was failing because it would pass the comparator (value, value), which the comparator likely did not expect, and thus would typically return NaN or undefined.

So instead, we now perform the bisection without first testing for orderability, but if the final comparison at the end of bisection returns NaN, we know that the search value was not orderable. In the case that the search value is not orderable, this is obviously slower since we still perform the bisection, but we don’t need to optimize for this case and it’s better to be correct and backwards compatible. We can’t do this; see comment below.

So instead, we now limit the search value orderability test to when the bisector uses an accessor or a known symmetric comparator (specifically d3.ascending and d3.descending, which are used by d3.bisectLeft et al.). It’s a bummer that this means we can’t enforce that non-orderable values go to the end in the general case, but I don’t think it’s possible given the constraints, at least not without breaking backwards compatibility and requiring the use of symmetric comparators (or accessors).

@mbostock
Copy link
Member Author

mbostock commented Apr 9, 2022

Hmm, I just realized that this won’t work because the array could only have non-orderable values in it, and thus the last comparison could return NaN even if the search value is orderable. For example, this now fails:

const values = [null, undefined, NaN];
assert.strictEqual(bisectLeft(values, 1), 0);
assert.strictEqual(bisectLeft(values, 2), 0);

If the comparator is specified as a function that takes (object, value), there’s no way for us to test just an object for orderability (because we can’t construct a synthetic value to test against), just like we can’t test just a value for orderability (because we can’t construct a synthetic object to test against). So… I think we might be stuck here, although we could special case specific comparators that we know to be symmetric. 🤔

@mbostock mbostock marked this pull request as ready for review April 9, 2022 00:38
@mbostock mbostock force-pushed the mbostock/fix-comparator-bisector branch from 3888d56 to 6be94fe Compare April 9, 2022 00:38
@mbostock mbostock merged commit 4e126a7 into main Apr 11, 2022
@mbostock mbostock deleted the mbostock/fix-comparator-bisector branch April 11, 2022 19:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

bisector no longer supports two-argument (object, value) comparator
2 participants