2015-09-04 4 views
1

Мне нужно получить 20 результатов из ленивой последовательности миллионов хеш-карт, но для 20 будет основано на сортировке по различным значениям внутри хэш-карт.сортировать по ленивой последовательности хеш-карт в clojure

Например:

(def population [{:id 85187153851 :name "anna" :created #inst "2012-10-23T20:36:25.626-00:00" :rank 77336} 
     {:id 12595145186 :name "bob" :created #inst "2011-02-03T20:36:25.626-00:00" :rank 983666} 
     {:id 98751563911 :name "cartmen" :created #inst "2007-01-13T20:36:25.626-00:00" :rank 112311} 
     ... 
     {:id 91514417715 :name "zaphod" :created #inst "2015-02-03T20:36:25.626-00:00" :rank 9866}] 

В нормальных условиях простой sort-by бы получить работу:

(sort-by :id population) 
(sort-by :name population) 
(sort-by :created population) 
(sort-by :rank population) 

Но мне нужно сделать это через миллионы записей как можно быстрее и хотите лениво, а не реализовать весь набор данных.

Я много огляделся и нашел ряд реализаций алгоритмов, которые очень хорошо работают для сортировки последовательности значений (в основном числовых), но не для ленивой последовательности хэш-карт так, как мне нужно.

(ns joy.q) 

(defn sort-parts 
    "Lazy, tail-recursive, incremental quicksort. Works against 
    and creates partitions based on the pivot, defined as 'work'." 
    [work] 
    (lazy-seq 
    (loop [[part & parts] work] 
    (if-let [[pivot & xs] (seq part)] 
     (let [smaller? #(< % pivot)] 
     (recur (list* 
       (filter smaller? xs) 
       pivot 
       (remove smaller? xs) 
       parts))) 
     (when-let [[x & parts] parts] 
     (cons x (sort-parts parts))))))) 

(defn qsort [xs] 
    (sort-parts (list xs))) 

работает очень хорошо ...

(time (take 10 (qsort (shuffle (range 10000000))))) 
"Elapsed time: 551.714003 msecs" 
(0 1 2 3 4 5 6 7 8 9) 

Отлично! Но ...

Как бы я ни старался, я не могу понять, как применить это к последовательности хэшмапов.

мне нужно что-то вроде:

(take 20 (qsort-by :created population)) 

ответ

4

Если вам нужны только верхние N элементов полной сортировки слишком дорого (даже ленивый вид, как один в JOC: он должен держать Почти все данные установлен в память).

Вам нужно всего лишь сканировать (reduce) набор данных и сохранить лучшие элементы N до сих пор.

=> (defn top-by [n k coll] 
    (reduce 
     (fn [top x] 
     (let [top (conj top x)] 
      (if (> (count top) n) 
      (disj top (first top)) 
      top))) 
     (sorted-set-by #(< (k %1) (k %2))) coll)) 
#'user/top-by 
=> (top-by 3 first [[1 2] [10 2] [9 3] [4 2] [5 6]]) 
#{[5 6] [9 3] [10 2]} 
+0

Фантастический! Спасибо! – adamneilson