From 3baa7c12a503efeee9e4d19e6ad9ca0fcd6242ea Mon Sep 17 00:00:00 2001
From: Simon Brooke <simon@journeyman.cc>
Date: Sun, 30 Sep 2018 14:38:57 +0100
Subject: [PATCH] Order-preserving-set

---
 src/adl_support/rest_support.clj | 14 +++++---------
 src/adl_support/utils.clj        | 14 ++++++++++++++
 test/adl_support/utils_test.clj  |  6 ++++++
 3 files changed, 25 insertions(+), 9 deletions(-)

diff --git a/src/adl_support/rest_support.clj b/src/adl_support/rest_support.clj
index 56c1ade..8f6cafa 100644
--- a/src/adl_support/rest_support.clj
+++ b/src/adl_support/rest_support.clj
@@ -46,10 +46,10 @@
   ;; TODO: candidate for moving to adl-support.core
   [form request]
   `(if-valid-user
-    ~form
-    ~request
-    {:status 403
-     :body (json/write-str "You must be logged in to do that")}))
+     ~form
+     ~request
+     {:status 403
+      :body (json/write-str "You must be logged in to do that")}))
 
 
 (defmacro with-params-or-error
@@ -64,9 +64,6 @@
       :body (json/write-str (str "The following params are required: " ~required))}))
 
 
-;; (with-params-or-error (/ 1 0) {:a 1 :b 2} #{:a :b :c})
-;; (with-params-or-error "hello" {:a 1 :b 2} #{:a :b })
-
 (defmacro do-or-server-fail
   "Evaluate this `form`; if it succeeds, return an HTTP response with this
   status code and the JSON-formatted result as body; if it fails, return an
@@ -78,6 +75,5 @@
        {:status ~status
         :body (:result r#)}
        {:status 500
-             :body r#})))
-
+        :body r#})))
 
diff --git a/src/adl_support/utils.clj b/src/adl_support/utils.clj
index b22cbf1..2d1b8d2 100644
--- a/src/adl_support/utils.clj
+++ b/src/adl_support/utils.clj
@@ -658,3 +658,17 @@
         0
         (expt 10 v)))
     (catch Exception _ 0)))
+
+
+(defn order-preserving-set
+  "The Clojure `set` function does not preserve the order in which elements are
+  passed to it. This function is like `set`, except
+  1. It returns a list, not a hashset, and
+  2. It is order-preserving."
+  [collection]
+  (loop [lhs (list (first collection))
+         rhs (rest collection)]
+    (cond
+      (empty? rhs) (reverse lhs)
+      (some #(= (first rhs) %) lhs) (recur lhs (rest rhs))
+      true (recur (cons (first rhs) lhs) (rest rhs)))))
diff --git a/test/adl_support/utils_test.clj b/test/adl_support/utils_test.clj
index 4499eb7..5132672 100644
--- a/test/adl_support/utils_test.clj
+++ b/test/adl_support/utils_test.clj
@@ -598,3 +598,9 @@
       (is (= (key-names e2) #{"id" "shard"}))
       (is (= (key-names e2 true) #{:id :shard})))))
 
+
+(deftest order-preserving-set-tests
+  (testing "order-preserving-set"
+    (is (= '(:a) (order-preserving-set '(:a :a :a :a))))
+    (is (= '(:a) (order-preserving-set [:a :a :a :a])))
+    (is (= '(:a :b :c :d :e) (order-preserving-set '(:a :a :b :c :a :b :d :c :e))))))