-
-
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
base: master
Are you sure you want to change the base?
Changes from 4 commits
39856b8
39ffec8
bdbc9bd
c64e5d8
8a04f02
2c17b4e
667d380
be8e07c
3aca120
60038bb
7c618ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -828,16 +828,75 @@ | |
return this.models[index]; | ||
}, | ||
|
||
// Perform a binary search for a model in a sorted collection. | ||
search: function (toFind, options) { | ||
if (!this.comparator) throw new Error('Cannot search an unsorted Collection'); | ||
options || (options = {}); | ||
|
||
// Create `compare` function for searching. | ||
var compare; | ||
if (_.isFunction(toFind)) { | ||
// The user has provided the `compare` function. | ||
compare = 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. No binding? 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. true, probably worth adding |
||
} 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) { | ||
var modelResult = this.comparator(model); | ||
return valResult === modelResult ? 0 : (valResult > modelResult ? 1 : -1); | ||
}, 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) { | ||
var modelValue = model.get(this.comparator); | ||
return toFind === modelValue ? 0 : (toFind > modelValue ? 1 : -1); | ||
}, 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 (options.getMax && index < max && compare(this.at(index + 1)) === 0) { | ||
min = index + 1; | ||
} else if (options.getMin && index > min && compare(this.at(index - 1)) === 0) { | ||
max = index - 1; | ||
} else { | ||
found = true; | ||
} | ||
} | ||
if (!found) return options.returnIndex ? -1 : void 0; | ||
return options.returnIndex ? index : this.at(index); | ||
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. 👎 on this point. Just return the index 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. You may have a good point - what is your reasoning? 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. Clearer API, and easier to document. Let the user call |
||
}, | ||
|
||
// 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)) { | ||
return first ? void 0 : []; | ||
} else if (!_.isEqual(_.keys(attrs), [this.comparator])) { | ||
return this[first ? 'find' : 'filter'](function(model) { | ||
for (var key in attrs) { | ||
if (attrs[key] !== model.get(key)) return false; | ||
} | ||
return true; | ||
}); | ||
} else if (first) { | ||
return this.search(attrs[this.comparator], {getMin: true}); | ||
} else { | ||
var minIndex = this.search(attrs[this.comparator], {returnIndex: true, getMin: true}); | ||
var maxIndex = this.search(attrs[this.comparator], {returnIndex: true, getMax: true}); | ||
return minIndex == null ? [] : this.models.slice(minIndex, maxIndex + 1); | ||
} | ||
}, | ||
|
||
// Return the first model with matching attributes. Useful for simple cases | ||
|
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.