-
-
Notifications
You must be signed in to change notification settings - Fork 5.4k
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
Added Binary Search for Sorted Collections #3207
Open
joshuabambrick
wants to merge
11
commits into
jashkenas:master
Choose a base branch
from
joshuabambrick:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
39856b8
added binary search for collections
joshuabambrick 39ffec8
fixed binary search
joshuabambrick bdbc9bd
alter code to better fit previous style
joshuabambrick c64e5d8
add style comment backticks
joshuabambrick 8a04f02
bind `toFind` and only return index
joshuabambrick 2c17b4e
fixed bugs and tests from last commit
joshuabambrick 667d380
prevent setting both `getMin` and `getMax`
joshuabambrick be8e07c
test _ to 1.7.0, use _.comparator, getMax bool
joshuabambrick 3aca120
use _.matches in `where`
joshuabambrick 60038bb
use local comparator, reset underscore.js version
joshuabambrick 7c618ed
add new line to underscore.js
joshuabambrick File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -629,6 +629,18 @@ | |
var setOptions = {add: true, remove: true, merge: true}; | ||
var addOptions = {add: true, remove: false}; | ||
|
||
// Compare two values, in a `sort`-consistent way | ||
var compareVals = function(a, b) { | ||
if (a === b) return 0; | ||
var isAComparable = a >= a, isBComparable = b >= b; | ||
if (isAComparable || isBComparable) { | ||
if (isAComparable && !isBComparable) return -1; | ||
if (isBComparable && !isAComparable) return 1; | ||
} | ||
return a > b ? 1 : (b > a) ? -1 : 0; | ||
}; | ||
|
||
|
||
// Define the Collection's inheritable methods. | ||
_.extend(Collection.prototype, Events, { | ||
|
||
|
@@ -828,16 +840,68 @@ | |
return this.models[index]; | ||
}, | ||
|
||
// Perform a binary search for a model in a sorted collection. | ||
search: function (toFind, getMax) { | ||
if (!this.comparator) throw new Error('Cannot search an unsorted Collection'); | ||
|
||
// Create `compare` function for searching. | ||
var compare; | ||
if (_.isFunction(toFind)) { | ||
// The user has provided the `compare` function. | ||
compare = _.bind(toFind, this); | ||
} else if (_.isFunction(this.comparator)) { | ||
// Use the `comparator` function, `toFind` is a model. | ||
if (this.comparator.length === 2) { | ||
compare = _.bind(this.comparator, this, toFind); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. clever! |
||
} else { | ||
compare = _.bind(function (valResult, model) { | ||
return compareVals(valResult, this.comparator(model)); | ||
}, this, this.comparator(toFind)); | ||
} | ||
} else { | ||
// `comparator` is a string indicating the model property used to sort | ||
// `toFind` is the value sought. | ||
compare = _.bind(function (model) { | ||
return compareVals(toFind, model.get(this.comparator)); | ||
}, this); | ||
} | ||
|
||
// Perform binary search. | ||
var found = false, max = this.length - 1, min = 0, index, relValue; | ||
while (max >= min && !found) { | ||
index = Math.floor((max + min) / 2); | ||
relValue = compare(this.at(index)); | ||
if (relValue > 0) { | ||
min = index + 1; | ||
} else if (relValue < 0) { | ||
max = index - 1; | ||
} else if (getMax && index < max && compare(this.at(index + 1)) === 0) { | ||
min = index + 1; | ||
} else if (!getMax && index > min && compare(this.at(index - 1)) === 0) { | ||
max = index - 1; | ||
} else { | ||
found = true; | ||
} | ||
} | ||
|
||
return found ? index : -1; | ||
}, | ||
|
||
// Return models with matching attributes. Useful for simple cases of | ||
// `filter`. | ||
where: function(attrs, first) { | ||
if (_.isEmpty(attrs)) return first ? void 0 : []; | ||
return this[first ? 'find' : 'filter'](function(model) { | ||
for (var key in attrs) { | ||
if (attrs[key] !== model.get(key)) return false; | ||
} | ||
return true; | ||
}); | ||
if (_.isEmpty(attrs) || !_.isEqual(_.keys(attrs), [this.comparator])) { | ||
var matches = _.matches(attrs); | ||
return this[first ? 'find' : 'filter'](function(model) { | ||
return matches(model.attributes); | ||
}); | ||
} else if (first) { | ||
return this.at(this.search(attrs[this.comparator])); | ||
} else { | ||
var minIndex = this.search(attrs[this.comparator]); | ||
var maxIndex = this.search(attrs[this.comparator], true); | ||
return minIndex == null ? [] : this.models.slice(minIndex, maxIndex + 1); | ||
} | ||
}, | ||
|
||
// Return the first model with matching attributes. Useful for simple cases | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
is this broken for the
_.isFunction(toFind)
caseThere 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.
Well no, if the collection isn't sorted then it isn't sorted. I suppose you could allow this to pass in the case that the user has sorted the collection himself. I believe the thinking behind the
_.isFunction(toFind)
case was that if you were, for example, looking for a model who had a timestamp within a certain range (say a week or day) in a collection sorted by the timestamps property.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.
I don't really like the
toFind
function feature, to easy to introduce inconsistencies in dev code if theres any inconsitency with toFind and comparitor. 👎 on thatThere 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.
I suppose I also thought that this was a good idea for the case that the comparator is a function. The user needs to have a model which matches equal to that he is searching for, whereas supplying the
compare
function, allows him to search specifically for models which match certain criteria without having to create a model to do it if one is not at hand.