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

sort accessor #183

Merged
merged 1 commit into from
Dec 3, 2020
Merged

sort accessor #183

merged 1 commit into from
Dec 3, 2020

Conversation

mbostock
Copy link
Member

@mbostock mbostock commented Dec 1, 2020

Fixes #182.

@mbostock mbostock requested a review from Fil December 1, 2020 22:20
Copy link
Member

@Fil Fil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's probably about 99% of usage.

A question is, since it allocates a new array, might it not end up slower or triggering garbage collection, which the comparator version doesn't?

d3.sort(data, (a, b) => d3.ascending(a.value, b.value))
```

The *accessor* is only invoked once per element, and thus may be nondeterministic.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this we could almost use the initial index: d3.sort(A, function(d) { return -arguments[1]; }) works—the only caveat is we can't write function(d,i) since it would have a length===2.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, but I’m not understanding this comment. This:

d3.sort(A, function(d) { return -arguments[1]; })

This is an elaborate equivalent to this?

d3.reverse(A)

To avoid the length ambiguity we could introduce a separate method, say d3.sortBy, that takes an accessor rather than a comparator. But I think it’s convenient to use the same length test that d3.bisector used. I don’t think you’ll often want to reference the original index in an accessor, and if you did, you could always just do the sorting yourself.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes the example was too simple—what I had in mind was more "sorting with reference to an external array of values". But it was more a comment in passing than a specific concern.

if (typeof values[Symbol.iterator] !== "function") throw new TypeError("values is not iterable");
return Array.from(values).sort(comparator);
values = Array.from(values);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not exactly related to this commit, but aren't we losing something by using plain arrays?

In many cases (such as the optimal transport) I'm sorting a Float32Array of indices against values that I'm reading somewhere else.

To take a simple example, I want to sort all the pixels of an image based on their red value. I'd have indices = Float32Array.from({length: w * h}, (_,i) => i) and would want to do sorted = d3.sort(indices, i => imageData.data[4*i]). I'd rather have sorted be a Float32Array (and maybe the intermediate f, but I don't think it's generically possible since the accessor might return a string).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We discussed type preservation of the input array previously in #170 (comment), and I still think it’s appropriate that these array methods always return plain arrays for generality and simplicity. If you want something fancier, you can call array.sort directly.

@mbostock
Copy link
Member Author

mbostock commented Dec 3, 2020

Yes, I expect it might be slower than a simple accessor, but I think it’s more important that it does the right thing even if the accessor is nondeterministic. And the only way to do that is to materialize the values. You can always call array.sort or pass a comparator if you care about performance.

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.

d3.sort, but with an accessor (length = 1) instead of a comparator.
2 participants