-
Notifications
You must be signed in to change notification settings - Fork 121
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
Filter beatmap sets based on conditional searches (eg: AR>9) #323
base: master
Are you sure you want to change the base?
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.
Thanks, this is a feature I've always meant to get to! This mostly works, but I've suggested a few design changes in the inline comments to make this cleaner and more maintainable. Let me know if you have any questions!
Please make sure to test everything you can find that calls any method of BeatmapSet
(size()
, get()
, remove()
, etc.). I'm pretty sure there are some annoying cases with deleting beatmaps that this will break, but I haven't looked into it yet.
@@ -43,24 +45,35 @@ public BeatmapSet(ArrayList<Beatmap> beatmaps) { | |||
} | |||
|
|||
/** | |||
* Returns the number of elements. | |||
* Returns the number of search results or total number. |
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.
Returns the number of elements (possibly filtered).
*/ | ||
public int size() { return beatmaps.size(); } | ||
public int size() { return filteredBeatmaps.size() > 0 ? filteredBeatmaps.size() : beatmaps.size(); } |
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.
Why do you conditionally return the unfiltered size here -- wouldn't that cause problems? (But check that it doesn't break things if you remove it.)
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.
At the moment how it is written is that filtered list starts off empty, and then maps are added to the filter. If the filter list is empty then it is assumed no filter is applied so therefore the unfiltered size should be returned, else other parts of the program will think there's no maps at all.
I can change it so that instead filteredBeatmaps is a clone of beatmaps at first and then remove maps based on the criteria, that way it can always return filteredBeatmaps.size/get
/** | ||
* Returns the true number of elements | ||
*/ | ||
public int trueSize(){return beatmaps.size(); } |
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.
Would prefer a more meaningful name like unfilteredSize()
.
public int size() { return filteredBeatmaps.size() > 0 ? filteredBeatmaps.size() : beatmaps.size(); } | ||
|
||
/** | ||
* Returns the true number of elements |
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.
Returns the number of elements (unfiltered).
|
||
/** | ||
* Returns the beatmap at the given index. | ||
* @param index the beatmap index | ||
* @throws IndexOutOfBoundsException if the index is out of range | ||
*/ | ||
public Beatmap get(int index) { return beatmaps.get(index); } | ||
public Beatmap get(int index) { return filteredBeatmaps.size() > 0 ? filteredBeatmaps.get(index) : beatmaps.get(index); } |
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.
Again, why would we want the unfiltered get()
here?
* Clears the search array so we correctly reset the set after changing a filter | ||
*/ | ||
public void clearFilter(){ | ||
filteredBeatmaps.clear();} |
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.
Fix the formatting here. :P
* Checks whether the beatmap set matches a given search query. | ||
* @param query the search term | ||
* @return true if title, artist, creator, source, version, or tag matches query | ||
*/ | ||
public boolean matches(String query) { | ||
// search: title, artist, creator, source, version, tags (first beatmap) | ||
filteredBeatmaps.clear(); |
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.
matches()
shouldn't mutate any state (or if it does, that should be documented, since it's counterintuitive). It makes more sense to call clearFilter()
outside of this method.
@@ -153,6 +173,8 @@ public boolean matches(String query) { | |||
* @return true if the condition is met | |||
*/ | |||
public boolean matches(String type, String operator, float value) { | |||
filteredBeatmaps.clear(); |
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.
Same as above.
return true; | ||
} | ||
if (met){ | ||
filteredBeatmaps.add(beatmap); |
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.
You could write a (very similar) method filter(type, operator, value)
that sets your filteredBeatmaps
list. It's more redundant but still cleaner than this approach IMO.
nodes = groupNodes = BeatmapGroup.current().filter(parsedNodes); | ||
expandedIndex = -1; | ||
expandedStartNode = expandedEndNode = null; | ||
lastQuery = null; | ||
} | ||
|
||
/** | ||
* Will clear the filter on any mapsets that matched the last search request | ||
*/ | ||
private void resetFiltered(){ |
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.
For the sake of clarity, I think it's better to just call clearFilter()
on everything in nodes
, and not bother keeping the extra lastSearchNodes
reference. It's not noticeably helping performance, and I don't want this class getting much more complex (it's already pretty confusing).
Alternatively, since lastSearchNodes
is just pointing to nodes
anyway, why not use a boolean flag instead?
In osu! you can search for maps via conditionals like AR>=9 and only maps that match that criteria are returned.
opsu has the same functionality however the entire set is returned, a majority of sets are going to contain maps with things like stars>4 etc so returning the entire set makes it hard to see exactly what you're after.
This change will filter the beatmaps within a beatmap set on conditional searches so that only matches based on the condition are shown in game.