From efe273e32bf7b3498c31674be7b88878785c1035 Mon Sep 17 00:00:00 2001 From: "Robert C. Martin" Date: Fri, 8 Apr 2022 12:28:30 -0500 Subject: [PATCH] Better sorting technique, using sorted-set. I expect that will do insertions rather than continual sorting of a large collection. Also reversed the sense of the sort, so latest articles are on top. --- project.clj | 1 - spec/more_speech/nostr/events_spec.clj | 42 +++++++++++++++++++++--- src/more_speech/nostr/events.clj | 44 +++++++++++++------------- src/more_speech/ui/application.clj | 6 ++-- src/more_speech/ui/header_window.clj | 2 +- 5 files changed, 63 insertions(+), 32 deletions(-) diff --git a/project.clj b/project.clj index 9d90777..6cae034 100644 --- a/project.clj +++ b/project.clj @@ -4,7 +4,6 @@ :license {:name "Eclipse Public License" :url "http://www.eclipse.org/legal/epl-v10.html"} :dependencies [[org.clojure/clojure "1.10.3"] - ;[quil "3.1.0-SNAPSHOT"] [quil "4.0.0-SNAPSHOT"] [org.clojure/data.json "2.4.0"] [org.clojure/core.async "1.5.648"] diff --git a/spec/more_speech/nostr/events_spec.clj b/spec/more_speech/nostr/events_spec.clj index f6bdd63..1af4fbc 100644 --- a/spec/more_speech/nostr/events_spec.clj +++ b/spec/more_speech/nostr/events_spec.clj @@ -15,7 +15,8 @@ "sig" "dddddd"}) (with state {:application {:text-event-map {} - :chronological-text-events []} + :chronological-text-events (make-chronological-text-events) + } }) (it "creates the map of text events by id" (let [state (process-text-event @state @event) @@ -24,7 +25,7 @@ event (get event-map 0xdeadbeef :not-in-map)] (should= 1 (count event-map)) (should= 1 (count text-events)) - (should= 0xdeadbeef (first text-events)) + (should= [0xdeadbeef @now] (first text-events)) (should= [0xdeadbeef] (keys event-map)) (should= 0xdeadbeef (:id event)) (should= 0xf00d (:pubkey event)) @@ -32,8 +33,7 @@ (should= "the content" (:content event)) (should= 0xdddddd (:sig event)) (should= [[:p "0001" "someurl"] - [:e "0002" "anotherurl"]] (:tags event)) - )) + [:e "0002" "anotherurl"]] (:tags event)))) (it "adds references to tagged articles." (let [state (assoc-in @state @@ -45,4 +45,38 @@ (should= [0xdeadbeef] (:references article))) ) + (context "sorted set for handling events" + (it "adds one element" + (let [state (add-event @state {:id 10 :created-at 0})] + (should= #{[10 0]} (get-in state [:application :chronological-text-events])) + (should= {10 {:id 10 :created-at 0}} (get-in state [:application :text-event-map])))) + + (it "adds two elements in chronological order, should be reversed" + (let [state (add-event @state {:id 10 :created-at 0}) + state (add-event state {:id 20 :created-at 1}) + ] + (should= [[20 1] [10 0]] (seq (get-in state [:application :chronological-text-events]))) + (should= {10 {:id 10 :created-at 0} + 20 {:id 20 :created-at 1}} (get-in state [:application :text-event-map]))) + ) + (it "adds two elements in reverse chronological order, should remain." + (let [state (add-event @state {:id 10 :created-at 1}) + state (add-event state {:id 20 :created-at 0}) + ] + (should= [[10 1] [20 0]] (seq (get-in state [:application :chronological-text-events]))) + (should= {10 {:id 10 :created-at 1} + 20 {:id 20 :created-at 0}} (get-in state [:application :text-event-map]))) + ) + + (it "adds two elements with equal ids" + (let [state (add-event @state {:id 10 :created-at 1}) + state (add-event state {:id 10 :created-at 0}) + event-map (get-in state [:application :text-event-map])] + (should= [[10 1]] (seq (get-in state [:application :chronological-text-events]))) + (should= 1 (count event-map)) + ) + ) + ) ) + + diff --git a/src/more_speech/nostr/events.clj b/src/more_speech/nostr/events.clj index f25440b..3d1136f 100644 --- a/src/more_speech/nostr/events.clj +++ b/src/more_speech/nostr/events.clj @@ -3,7 +3,6 @@ [clojure.data.json :as json] [more-speech.nostr.util :refer [hex-string->num]] [more-speech.ui.widget :as w] - [more-speech.ui.formatters :as f] )) (s/def ::id number?) (s/def ::pubkey number?) @@ -25,20 +24,20 @@ (defn process-event [{:keys [application] :as state} event] (let [{:keys [_articles nicknames]} application - name-of (fn [pubkey] (get nicknames pubkey pubkey)) + _name-of (fn [pubkey] (get nicknames pubkey pubkey)) [_name _subscription-id inner-event :as _decoded-msg] event - {:strs [_id pubkey created_at kind _tags content _sig]} inner-event] + {:strs [_id _pubkey _created_at kind _tags _content _sig]} inner-event] (condp = kind 0 (process-name-event state inner-event) 3 (do ;(printf "%s: %s %s %s\n" kind (f/format-time created_at) (name-of pubkey) content) - state) + state) 1 (do ;(printf "%s: %s %s %s\n" kind (f/format-time created_at) (name-of pubkey) (subs content 0 (min 50 (count content)))) (process-text-event state inner-event)) 4 (do ;(printf "%s: %s %s %s\n" kind (f/format-time created_at) (name-of pubkey) content) - state) + state) (do (prn "unknown event: " event) state)))) @@ -58,7 +57,7 @@ (defn process-references [state {:keys [id tags] :as _event}] (let [e-tags (filter #(= :e (first %)) tags) refs (map second e-tags) - refs (map hex-string->num (take 1 refs)) ;; Hack. Only the first reference is counted. + refs (map hex-string->num (take 1 refs)) ;; Hack. Only the first reference is counted. ] (loop [refs refs state state] @@ -84,22 +83,23 @@ :sig sig :tags (process-tags (get event "tags"))})) -(defn by-event-time [event-map id1 id2] - (let [event1 (get event-map id1) - event2 (get event-map id2)] - (< (get event1 :created-at) - (get event2 :created-at)))) +(defn add-event [state event] + (let [id (:id event) + time (:created-at event) + state (assoc-in state [:application :text-event-map id] event) + state (update-in state [:application :chronological-text-events] conj [id time])] + state)) (defn process-text-event [state event] (let [event (translate-text-event event) - id (:id event) - state (assoc-in state [:application :text-event-map id] event) - event-map (get-in state [:application :text-event-map] {}) - events (get-in state [:application :chronological-text-events] []) - events (conj events id) - events (sort (partial by-event-time event-map) events) - state (assoc-in state [:application :chronological-text-events] events) - ;state (update-in state [:application :chronological-text-events] conj id) - state (w/redraw-widget state [:application :header-window]) - ] - (process-references state event))) \ No newline at end of file + state (add-event state event) + state (w/redraw-widget state [:application :header-window])] + (process-references state event))) + +(defn chronological-event-comparator [[i1 t1] [i2 t2]] + (if (= i1 i2) + 0 + (compare t2 t1))) + +(defn make-chronological-text-events [] + (sorted-set-by chronological-event-comparator)) \ No newline at end of file diff --git a/src/more_speech/ui/application.clj b/src/more_speech/ui/application.clj index ba63e44..cdd1735 100644 --- a/src/more_speech/ui/application.clj +++ b/src/more_speech/ui/application.clj @@ -33,7 +33,7 @@ (s/def ::mouse-locked-to #(or (nil? %) (s/coll-of keyword?))) (s/def ::keyboard-focus #(or (nil? %) (s/coll-of keyword?))) (s/def ::nicknames (s/map-of number? string?)) -(s/def ::chronological-text-events (s/coll-of number?)) +(s/def ::chronological-text-events (s/coll-of (s/tuple [number? number?]) :kind set?)) (s/def ::text-event-map (s/map-of number? ::events/event)) (s/def ::open-thread (s/coll-of number? :kind set?)) (s/def ::this-update (s/coll-of ::path :kind set?)) @@ -96,7 +96,7 @@ :mouse-locked-to nil :keyboard-focus nil :nicknames {} - :chronological-text-events [] + :chronological-text-events (events/make-chronological-text-events) :text-event-map {} :open-thread #{} :header-window (map->text-window @@ -134,5 +134,3 @@ :controls (->edit-window-controls)}) ) )) - - diff --git a/src/more_speech/ui/header_window.clj b/src/more_speech/ui/header_window.clj index fabf9e9..728e861 100644 --- a/src/more_speech/ui/header_window.clj +++ b/src/more_speech/ui/header_window.clj @@ -286,7 +286,7 @@ (defn get-threaded-events [application] (let [event-map (:text-event-map application) - events (:chronological-text-events application) + events (map first (:chronological-text-events application)) open-thread (:open-thread application) threaded-events (thread-events events event-map open-thread)] threaded-events