-
Notifications
You must be signed in to change notification settings - Fork 611
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
Keep original list order #16771
base: gh-pages
Are you sure you want to change the base?
Keep original list order #16771
Conversation
+1 |
Thanks for the PR, and for spotting the issue! Also, great to see a PR that actually adds tests for the behavior too! |
456a969
to
8a0c766
Compare
@LeaVerou , I've updated the PR to reflect your suggestions. My personal view:
|
now also improved testing - getListFromResults() will produce an array from the list elements output, to aid in comparing with original input array. this helper function should be moved to the shared file created by PR #16776 . |
I had the same use case recently: I want to preserve the order from the server. I made the following modification in diff --git a/awesomplete.js b/awesomplete.js
index a422d1b..872b7e2 100644
--- a/awesomplete.js
+++ b/awesomplete.js
@@ -219,11 +219,16 @@ _.prototype = {
// Populate list with options that match
this.ul.innerHTML = "";
- this._list
+ var filtered = this._list
.filter(function(item) {
return me.filter(item, value);
})
- .sort(this.sort)
+
+ var sorted = this.sort.length === 1
+ ? this.sort(filtered)
+ : filtered.sort(this.sort);
+
+ sorted
.every(function(text, i) {
me.ul.appendChild(me.item(text, value)); This allowed me to use the following sorting function (while still being able to use the default sorting functions if I wanted to): function sort(list) {
return list
.map((item, index) => {
return {
item: rank(item),
index,
original: item,
}
})
.sort(stableSort((a, b) => b - a))
.map(({original}) => original)
}
function stableSort(fn) {
return (a, b) => {
let value = fn(a.item, b.item)
return value === 0 ? a.index - b.index : value
}
}
function rank(item) {
// expensive function
} In my opinion, it is up to the user to make sure the sorting is stable, just like I did above. Also note how I could move my expensive Thoughts? |
@lydell thanks for sharing your solution.
From a technical perspective, I'd prefer the first or third approach. Just my thoughts :) |
Nice summary, @pazams. I have one thing I’d like to add to it, though. Only the third approach allows me to move my expensive |
@lydell - it might be so. I've edited my earlier response to reflect that technically the 1st and 3rd are some what equivalent. The 3rd option is a generalized 1st option, and might be better for when someone would need a stable sort applied on a compare function other than just keeping the original list order. Though I can't think of a use case for a stable sort with a compare function other than |
@pazams My compare function mostly returns 0 (to keep server order, which requires a stable sort), but in a few special cases does not. |
I'm kind of late to the party, but was this already considered? // to keep the original list order use:
new Awesomplete(input, { sort: false });
// otherwise API user should be aware of browser's sort behavior and deal with it
new Awesomplete(input, { sort: function (a, b) { return 0; } }); There are for sure lots of existing stable sort libs, so why not only introduce one more, but also add it to Awesomplete codebase? The code change required to be able to keep list order can be as simple as: diff --git a/awesomplete.js b/awesomplete.js
index e2a8341..32effcd 100644
--- a/awesomplete.js
+++ b/awesomplete.js
@@ -220,12 +220,12 @@ _.prototype = {
// Populate list with options that match
this.ul.innerHTML = "";
- this._list
+ var items = this._list
.filter(function(item) {
return me.filter(item, value);
- })
- .sort(this.sort)
- .every(function(text, i) {
+ });
+ if (this.sort) items = items.sort(this.sort);
+ items.every(function(text, i) {
me.ul.appendChild(me.item(text, value));
return i < me.maxItems - 1; |
@vlazar, yes, your code suggestion was how I coded the original PR:
It too applied the sorting if |
@pazams I'm sad this goes in "more helpers" direction. More code means more place for bugs. Having tests for original PR intention (Keep original list order) is nice, but sort algorithm probably needs extensive coverage. And good tests for sort implementation is kind of project of it's own. @LeaVerou If you are willing to add sort helper anyway, why not use external module and maybe bundle it at build step? This way it can be developed and supported in it's own repo. Also, such module probably already exists. |
As an afterthought, maybe we need to think about introducing awesomplete plugins? |
@vlazar, I too would've just preferred to just conditionally apply the sort, and stay away from stable sorting. |
@pazams Do you propose being able to choose between not sorting at all and sorting using a possibly unstable sort (depending on the browser)? So everyone needing a stable sort are left to using a fork? |
@lydell, I'm suggesting that the code consuming Awesomplete can stable sort the list on his part. Then, Awesomplete only needs to allow to keep the original list order, and everyone is happy. I looked at AngularJS as a reference for a library that exposes a sorting functionality ("orderBy") In the past:
In the present: however: Thoughts? |
I agree 100% on this. That's why I suggested just that in my first comment. However, the proposal in your second to last post does not seem to allow for a stable sort. If it does, could you please explain how? |
Yes, we do want Awesomplete to be small, but not buggy, and this is a bug and there are small stable sorts around. |
But I’d still like to be able to do By the way, this is a pretty minimal stable sort: // Possible unstable:
array.sort(sortFn)
// Stable:
array
.map((item, index) => {item, index})
.sort((a, b) => sortFn(a.item, b.item) || a.index - b.index)
.map(({item}) => item) |
yes, consider this: var input = document.getElementById("myinput");
var data = getData();
var sortedData = mySortingAlgorithm(data);
awesompleter = new Awesomplete(input,{sort: false});
awesompleter.list = sortedData; as for the stable sort snippet on your last comment- it is very elegant. However, the technique of wrapping the elements and sorting on wrapped objects is very slow |
@@ -41,5 +41,12 @@ var shared = { | |||
expect(li.getAttribute('aria-selected')).toBe('true'); | |||
expect(shared.awesompleter.status.textContent).toBe(li.textContent); | |||
} | |||
}, | |||
getListFromResults: function (awesompleter){ |
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.
Looks like an awesomplete API needs some tweaks (not only for tests). See my comment here #16725 (comment)
Thoughts?
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.
+1
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.
Idea about splitting list
setter/getter API #16790 (comment)
Any updates on this? |
Consider the list's data originated from data fetched by an AJAX call.
The data is sorted by the server with a unique strategic algorithm (e.g a locations auto complete service sorted by distance from user).
There should be a way to maintain the server's list order.
Using the compare function:
function (a,b){return 0;}
won't work.That's because
Array.prototype.sort
is not a stable sort.from http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.11
"sort is not necessarily stable (that is, elements that compare equal do not necessarily remain in their original order)"
This unstable sort behaviour can be demonstrated in arrays above 10 items in a chrome browser:
http://plnkr.co/edit/8Chk6VB2eR7WPfSNYVRl?p=preview
To fix this issue in Awesomplete, we can either:
evaluate()
, and conditionally apply.sort
.Array.prototype
with a function to wrapArray.prototype.sort
, and conditionally apply.sort
.This pull request implements the 1st solution, to avoid extending Array.prototype with it's potential conflicts.