2014-09-21 12 views
6

Underscore bietet die Funktion sortBy, um ein Array von Objekten zu sortieren. Wenn ich dieses sortierte Array jedoch einmal habe, gibt es eine Möglichkeit, ein Element mithilfe der binären Suche zu finden? Die Funktion find nutzt nicht die Tatsache, dass das Array sortiert ist, während die Funktion indexOf dies tut, aber es bietet keine Möglichkeit, den Sortierschlüssel anzugeben.Underscore - Finden Sie aus einem sortierten Array von Objekten

  1. Fehle ich etwas?
  2. Gibt es noch eine andere JS-Bibliothek, die dies ermöglicht?
+2

Neugierig: was Sie tun, dass eine binäre Suche den Unterschied zwischen lenkbar und inakzeptabel macht? Wie groß ist Ihr Array und was machen Sie damit? –

+0

Eine binäre Suche muss in einem sortierten Array durchgeführt werden. Da Sie ein Array von Objekten haben, benötigen Sie höchstwahrscheinlich eine benutzerdefinierte Lösung. –

+0

Das Array selbst ist etwa 1000 Elemente, aber ich muss viele Elemente darin finden. Eine lineare Suche dieses Arrays in einer Schleife ist daher möglicherweise nicht sehr effizient. – Naresh

Antwort

3

Die Funktion _.sortedIndex wird für eine binäre Suche verwendet, ist aber etwas allgemeiner als Ihr Zweck. Ich würde es nur eine sortedFind bauen verwenden, um zum Beispiel:

_.sortedFind = function sortedFind(list, item, key) { 
    return (_.isEqual(item, list[_.sortedIndex(list, item, key)])); 
} 

Verwendungsbeispiel:

// http://jsfiddle.net/w3hzrehy/ 
_.sortedFind([10, 20, 30, 40, 50], 10); // true 

var stooges = [{name: 'moe', age: 40}, {name: 'curly', age: 60}]; 
_.sortedFind(stooges, {name: 'larry', age: 50}, 'age'); // false 
_.sortedFind(stooges, {name: 'curly', age: 60}, 'age'); // true 
+0

Danke. Das ist sehr nah! Eigentlich wollte ich den Index mit einem Wert versehen finden. Also '_.sortedFind (stooges, {name: 'curly'}, 'name');' sollte den Index 1 zurückgeben. Ich glaube '_.sortedIndex (stooges, {name: 'curly'}, 'name'); 'tut genau das, obwohl ich es nicht den ganzen Artikel sende. Alles, was ich tun muss, ist zu überprüfen, ob der zurückgegebene Index tatsächlich den Artikel mit dem Namen 'curly' enthält. Die Unterstreichdokumente sind nicht sehr klar, aber ich denke, so soll es funktionieren. Kannst du bitte bestätigen? – Naresh

+0

Hier ist die Jsfiddle für das, was ich will: http://jsfiddle.net/w3hzrehy/16/ – Naresh

+0

@Naresh ja es sieht aus wie Sie 'sortierteIndex' korrekt verwenden, wenn Sie Ihre Funktion wiederverwenden wollen, möchten Sie vielleicht verallgemeinern es für funktionsbasierte und reine Wert sortiert: http://jsfiddle.net/w3hzrehy/18/ (Beachten Sie, ich musste Unterstreichung für den Zugriff auf die _iteratee() -Funktion zu aktualisieren.) – lossleader

1

Sie nichts fehlen. Es ist irgendwie überraschend, nicht wahr?

Google Closure Bibliothek tut Support-Funktionen innerhalb von binarysearch (Ich bin sicher, es gibt andere):

http://docs.closure-library.googlecode.com/git/namespace_goog_array.html

Sie würden es verwenden, wie Sie sich vorstellen würde:

var myArray = getPetArray(); 
goog.array.binarySearch(myArray, 'fido', function(pet) { return pet.name; }); 

Wenn Sie nicht noch eine andere Bibliothek ziehen möchten, ist die Quelle kurz und verfügbar:

http://docs.closure-library.googlecode.com/git/local_closure_goog_array_array.js.source.html#line989

Ich schneide und den wichtigen Teil hier bei Links Änderung einfügen - nur nicht vergessen, Kredit zu Google geben:

goog.array.binarySearch = function(arr, target, opt_compareFn) { 
    return goog.array.binarySearch_(arr, 
    opt_compareFn || goog.array.defaultCompare, false /* isEvaluator */, 
    target); 
}; 

goog.array.binarySearch_ = function(arr, compareFn, isEvaluator, opt_target, 
opt_selfObj) { 
    var left = 0; // inclusive 
    var right = arr.length; // exclusive 
    var found; 
    while (left < right) { 
    var middle = (left + right) >> 1; 
    var compareResult; 
    if (isEvaluator) { 
     compareResult = compareFn.call(opt_selfObj, arr[middle], middle, arr); 
    } else { 
     compareResult = compareFn(opt_target, arr[middle]); 
    } 
    if (compareResult > 0) { 
     left = middle + 1; 
    } else { 
     right = middle; 
     // We are looking for the lowest index so we can't return immediately. 
     found = !compareResult; 
    } 
    } 
    // left is the index if found, or the insertion point otherwise. 
    // ~left is a shorthand for -left - 1. 
    return found ? left : ~left; 
}; 

goog.array.defaultCompare = function(a, b) { 
    return a > b ? 1 : a < b ? -1 : 0; 
}; 
+0

Danke, das ist eine großartige Lösung. Die Lösung von @ lossleader war jedoch mit sehr wenig zusätzlichem Aufwand unter Verwendung von Unterstreichung implementierbar, daher habe ich dies als akzeptierte Antwort markiert. Danke nochmal für deine Hilfe. – Naresh

Verwandte Themen