2014-09-21 5 views
6

Underscore fornisce la funzione sortBy per ordinare una matrice di oggetti. Tuttavia, una volta che ho questo array ordinato, c'è un modo per trovare un elemento usando la ricerca binaria? La funzione find non fa leva sul fatto che l'array è ordinato, mentre la funzione indexOf lo fa, ma non fornisce un modo per specificare la chiave di ordinamento.Underscore - Trova da una matrice ordinata di oggetti

  1. Mi manca qualcosa?
  2. C'è qualche altra libreria JS che consente di farlo facilmente?
+2

curioso: che cosa stai facendo che una ricerca binaria fa la differenza tra trattabili e inaccettabile? Quanto è grande il tuo array e cosa stai facendo? –

+0

Una ricerca binaria deve essere eseguita su un array ordinato. Poiché hai una serie di oggetti, molto probabilmente avrai bisogno di una soluzione personalizzata. –

+0

L'array stesso è di circa 1000 articoli, ma ho bisogno di trovare elementi in esso molte volte. Quindi una ricerca lineare di questa matrice in un ciclo potrebbe non essere molto efficiente. – Naresh

risposta

3

La funzione _.sortedIndex viene utilizzata per una ricerca binaria, ma è un po 'più generica del proprio scopo. Vorrei solo usarlo per costruire una sortedFind, ad esempio:

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

Esempio di utilizzo:

// 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

Grazie. Questo è molto vicino! In realtà volevo trovare l'indice dato un valore. Quindi '_.sortedFind (stooges, {name: 'curly'}, 'name');' dovrebbe restituire l'indice 1. Credo in '_.sortedIndex (stooges, {name: 'curly'}, 'name'); 'fa esattamente questo anche se non gli mando l'articolo completo. Tutto quello che devo fare è verificare se l'indice restituito contiene effettivamente l'elemento con il nome "ricci". I documenti di sottolineatura non sono molto chiari su questo, ma penso che questo è come dovrebbe funzionare. Puoi confermare per favore? – Naresh

+0

Ecco il jsfiddle per quello che voglio: http://jsfiddle.net/w3hzrehy/16/ – Naresh

+0

@Naresh sì sembra che tu stia usando 'sortedIndex' correttamente, se hai intenzione di riutilizzare la tua funzione, potresti voler generalizzare per i tipi basati sulla funzione e sul valore puro: http://jsfiddle.net/w3hzrehy/18/ (nota che dovevo aggiornare il carattere di sottolineatura per l'accesso alla funzione _iteratee()). – lossleader

1

Non manca nulla. È un po 'sorprendente, no?

biblioteca

Google Chiusura fa le funzioni di supporto all'interno di binarySearch (sono sicuro che ci sono altri):

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

si dovrebbe utilizzare, proprio come ci si immagina:

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

Se non vuoi trascinare in un'altra libreria, la fonte è breve e disponibile:

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

taglio e incollo la parte importante nel caso in cui collega il cambiamento - basta ricordarsi di dare credito a Google:

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

Grazie, questa è un'ottima soluzione. Tuttavia, la soluzione di @ lossleader è stata implementabile con uno sforzo aggiuntivo minimo utilizzando il trattino basso, quindi l'ho contrassegnata come risposta accettata. Grazie ancora per il vostro aiuto. – Naresh