diff --git a/src/cljs/ulysses/utils.cljs b/src/cljs/ulysses/utils.cljs index 399e75a..26d6349 100644 --- a/src/cljs/ulysses/utils.cljs +++ b/src/cljs/ulysses/utils.cljs @@ -22,6 +22,9 @@ (defn boolean? [x] (or (true? x) (false? x))) +(defn lazy-seq? [x] + (instance? LazySeq x)) + (defn ptr "print x, then return it. useful for debugging inside threading macros or map" @@ -120,14 +123,18 @@ seqable))) (defn merge-by-id - "merge multiple seqables of maps by :id; the rightmost having the highest preference" - ([a b] - (let [new-ids (map :id b)] - (concat - (remove #(-> % :id hash-set (some new-ids)) a) - b))) - ([& seqables] - (reduce merge-by-id seqables))) + "merge multiple seqables of maps by :id; + the rightmost having the highest preference; + the result taking on the type of the first" + [a & more] + (let [list-or-lzs? ((some-fn list? lazy-seq?) a) + into-maybe (if list-or-lzs? identity (partial into (empty a))) + all (apply (partial concat a) more)] + (->> all + (group-by :id) + (vals) + (map last) + (into-maybe)))) (defn remove-by-id "remove a map by :id within a seqable (of maps)" diff --git a/test/cljs/ulysses/utils_test.cljs b/test/cljs/ulysses/utils_test.cljs index 4f381b3..51032a6 100644 --- a/test/cljs/ulysses/utils_test.cljs +++ b/test/cljs/ulysses/utils_test.cljs @@ -2,11 +2,11 @@ (:require [cljs.test :refer-macros [deftest testing are is]] [ulysses.utils :as utils])) -(deftest test-noop +(deftest noop (are [x] (nil? (apply utils/noop x)) [] [1] [1 2])) -(deftest test-boolean? +(deftest boolean? (are [x y] (= x (utils/boolean? y)) true true true false @@ -18,13 +18,13 @@ ; missing: ptr-first -(deftest test-metas-equal? +(deftest metas-equal? (is (utils/metas-equal? [(with-meta {:a 1} {:key 1})] [(with-meta {:a 2} {:key 1})])) (is (not (utils/metas-equal? [(with-meta {:a 1} {:key 1})] [(with-meta {:a 2} {:key 2})])))) -(deftest test-remap-and-variations +(deftest remap-and-variations (let [from {:a 1 :b 2 :c 3}] (testing "remap" (is (= {:a 2 :b 4 :c 6} @@ -40,10 +40,10 @@ (fn [k v] (keyword (str (name k) v))) from)))))) -(deftest test-hyphenate-keys +(deftest hyphenate-keys (is (= {:a-b 1 :c-d 2} (utils/hyphenate-keys {:a_b 1 :c_d 2})))) -(deftest test-map-subels +(deftest map-subels (testing "when values have ids" (let [in [{:id 23 :value 3} {:id 95 :value 4}] out (utils/map-subels :div in) @@ -66,7 +66,7 @@ (is (= out expected)) (is (utils/metas-equal? out expected))))) -(deftest test-str->int +(deftest str->int (testing "successful cases" (are [x y] (= x (utils/str->int y)) 0 "0" @@ -83,7 +83,7 @@ "garbage" "12moregarbage12" 123.123 nil))) -(deftest test-clj->json +(deftest clj->json (are [x y] (= x (utils/clj->json y)) "null" nil "true" true @@ -91,7 +91,7 @@ "[1,2,3]" [1 2 3] "[\"one\",\"three\",\"two\"]" (sorted-set :one :two :three))) -(deftest test-json->clj +(deftest json->clj (testing "basic values" (are [x y] (= x (utils/json->clj y)) nil "null" @@ -107,19 +107,19 @@ (are [x] (thrown? js/SyntaxError (utils/json->clj x)) "asdf" "[1,2"))) -(deftest test-url-encode-component +(deftest url-encode-component (are [x y] (= x (utils/url-encode-component y)) "foo" "foo" "foo%20bar" "foo bar" "%3F%26%3F%26" "?&?&")) -(deftest test-truncate-with-ellipsis +(deftest truncate-with-ellipsis (testing "normal" (is (= "foo..." (utils/truncate-with-ellipsis "foo bar" 3)))) (testing "custom ellipsis" (is (= "foo[..]" (utils/truncate-with-ellipsis "foo bar" 3 "[..]"))))) -(deftest test-find-by-id +(deftest find-by-id (let [ms [{:id 1} {:id 2} {:id 94}]] (testing "found (type int)" (are [pf y] (= (pf ms) (utils/find-by-id y ms)) @@ -134,3 +134,25 @@ (testing "not found" (are [x] (nil? (utils/find-by-id x ms)) 33 "33" "1.0" nil)))) + +(deftest merge-by-id + ;; ih test util, ex: (ih [1 2 3]) => [{:id 1} {:id 2} {:id 3}] + (let [ih (fn [coll] + ((if (list? coll) identity (partial into (empty coll))) + (map #(if (map? %) % (hash-map :id %)) coll)))] + (testing "two seq args" + (are [x a b] (= x (utils/merge-by-id (ih a) (ih b))) + [] [] [] + [] [] (list) + [{:id 0} {:id 1} {:id 2}] [0] [1 2] + #{{:id 0} {:id 1} {:id 2}} #{0} #{1 2} + #{{:id 0} {:id 1} {:id 2}} #{0} [1 2] + (list {:id 0} {:id 1 :a true} {:id 2} {:id 3}) (list 0 {:id 1 :a false} 2) [{:id 1 :a true} 2 3])) + (testing "many seq args" + (are [x many] (= x (apply utils/merge-by-id (map ih many))) + [] [[] [] []] + [] [[] (list) (list)] + [{:id 0}] [[] [0] [] [0]] + #{{:id 0} {:id 1} {:id 2}} [#{0} #{1 2} #{0 2 1}] + #{{:id 0} {:id 1} {:id 2 :a true}} [#{0} [1 2] #{{:id 2 :a true}}] + (list {:id 0} {:id 1} {:id 2 :a true} {:id 3}) [(list 0 1 2) [1 {:id 2 :a true} 3] []]))))