Вот один из способов сделать это. Я добавил комментарии на каждом этапе конвейера:
(def lines "date,description,
Mon Jul 25 14:34:49 EDT 2016, some entry,
Mon Jul 25 17:35:11 EDT 2016, some other entry,
Mon Jul 25 21:52:05 EDT 2016, yet another entry,
Mon Jul 25 22:35:59 EDT 2016, some entry,
Mon Jul 25 23:54:19 EDT 2016, some other entry,
Tue Jul 26 00:31:41 EDT 2016, some entry,
Wed Jul 27 15:45:08 EDT 2016, yet another entry,
Wed Jul 27 16:15:50 EDT 2016, some entry,")
(->> ; get the lines
lines
; split them
clojure.string/split-lines
; discard header
rest
; keep only the date
(map #(first (clojure.string/split % #",")))
; make them Date instances
(map #(Date. %))
; group them by yyyy-MM-dd
(group-by #(.format (SimpleDateFormat. "yyyy-MM-dd") %))
; make a map whose keys are {:date :count}
(reduce-kv #(assoc %1 %2 {:date %2 :count (count %3)}) {})
; keep only the values of this built map
vals)
=> ({:date "2016-07-25", :count 2} {:date "2016-07-26", :count 4} {:date "2016-07-27", :count 2})
Другой способ использования frequencies
вместо group-by
:
(->> ; get the lines
lines
; split them
clojure.string/split-lines rest
; keep the date
(map #(first (clojure.string/split % #",")))
; format them as yyyy-MM-dd
(map #(.format (SimpleDateFormat. "yyyy-MM-dd") (Date. %)))
; compute the frequencies
frequencies
; build a map out of it, this time you don't need to count yourself since frequencies did it for you
(reduce-kv #(assoc %1 %2 {:date %2 :count %3}) {})
; keep only the values of the map we built
vals)
=> ({:date "2016-07-25", :count 2} {:date "2016-07-26", :count 4} {:date "2016-07-27", :count 2})
По производительности они похожи, на моем компьютере для анализа 100 000 строк требуется около 2 секунд.
Если узким местом является проблема производительности, вы можете создать только один SimpleDateFormat
или даже отформатировать дату от строки до yyyy-MM-dd
, не создавая Date
вообще. Но только при необходимости производительность уже довольно приличная:)