# Specification: maps (associative). (use ../support/harness) (defspec "map / construct & predicate" ["literal" "{:a 1}" "{:a 1}"] ["hash-map" "{:a 1, :b 2}" "(hash-map :a 1 :b 2)"] ["empty" "{}" "{}"] ["map? true" "true" "(map? {:a 1})"] ["map? false on vector" "false" "(map? [1 2])"] ["count" "2" "(count {:a 1 :b 2})"] ["empty? true" "true" "(empty? {})"] ["equality order-indep" "true" "(= {:a 1 :b 2} {:b 2 :a 1})"]) (defspec "map / access" ["get" "1" "(get {:a 1} :a)"] ["get missing nil" "nil" "(get {:a 1} :z)"] ["get default" ":x" "(get {:a 1} :z :x)"] ["keyword as fn" "1" "(:a {:a 1})"] ["keyword fn default" ":x" "(:z {:a 1} :x)"] ["map as fn" "1" "({:a 1} :a)"] ["get-in" "2" "(get-in {:a {:b 2}} [:a :b])"] ["get-in missing" "nil" "(get-in {:a {}} [:a :b])"] ["contains? key" "true" "(contains? {:a 1} :a)"] ["contains? missing" "false" "(contains? {:a 1} :z)"] ["find returns entry" "[:a 1]" "(find {:a 1} :a)"] ["keys" "true" "(= #{:a :b} (set (keys {:a 1 :b 2})))"] ["vals" "true" "(= #{1 2} (set (vals {:a 1 :b 2})))"]) (defspec "map / update" ["assoc adds" "{:a 1, :b 2}" "(assoc {:a 1} :b 2)"] ["assoc overwrites" "{:a 9}" "(assoc {:a 1} :a 9)"] ["assoc many" "{:a 1, :b 2}" "(assoc {} :a 1 :b 2)"] ["dissoc" "{:a 1}" "(dissoc {:a 1 :b 2} :b)"] ["dissoc many" "{:a 1}" "(dissoc {:a 1 :b 2 :c 3} :b :c)"] ["merge" "{:a 1, :b 2}" "(merge {:a 1} {:b 2})"] ["merge overwrites" "{:a 2}" "(merge {:a 1} {:a 2})"] ["merge lattermost wins" "{:a 3}" "(merge {:a 1} {:a 2} {:a 3})"] ["merge no args -> nil" "nil" "(merge)"] ["merge all nil -> nil" "nil" "(merge nil nil)"] ["merge nil arg no-op" "{:a 1}" "(merge {:a 1} nil)"] ["merge nil then map" "{:a 1}" "(merge nil {:a 1})"] ["merge empty + nil" "{}" "(merge {} nil)"] ["merge map-entry (conj)" "{:a 1}" "(merge {} (first {:a 1}))"] ["merge [k v] vector" "{:foo 1}" "(merge {} [:foo 1])"] ["merge collection key" "true" "(= {[2 3] :foo} (merge {[2 3] :foo} nil {}))"] ["merge-with" "{:a 3}" "(merge-with + {:a 1} {:a 2})"] ["update" "{:a 2}" "(update {:a 1} :a inc)"] ["update missing w/ fnil" "{:a 1}" "(update {} :a (fnil inc 0))"] ["update-in" "{:a {:b 2}}" "(update-in {:a {:b 1}} [:a :b] inc)"] ["assoc-in" "{:a {:b 1}}" "(assoc-in {} [:a :b] 1)"] ["select-keys" "{:a 1}" "(select-keys {:a 1 :b 2} [:a])"] ["into onto map" "{:a 1, :b 2}" "(into {:a 1} [[:b 2]])"] ["zipmap" "{:a 1, :b 2}" "(zipmap [:a :b] [1 2])"]) (defspec "map / iteration & entries" ["map over entries" "true" "(= #{1 2} (set (map val {:a 1 :b 2})))"] ["map keys" "true" "(= #{:a :b} (set (map key {:a 1 :b 2})))"] ["reduce over entries" "6" "(reduce (fn [a e] (+ a (val e))) 0 {:a 1 :b 2 :c 3})"] ["reduce-kv" "6" "(reduce-kv (fn [a k v] (+ a v)) 0 {:a 1 :b 2 :c 3})"] ["destructure entry" "true" "(= [[:a 2]] (into [] (map (fn [[k v]] [k (inc v)]) {:a 1})))"] ["first of map is entry" "true" "(let [e (first {:a 1})] (and (= (key e) :a) (= (val e) 1)))"] ["map-entry?" "true" "(map-entry? (first {:a 1}))"] ["count of nil map" "0" "(count nil)"] ["get from nil" "nil" "(get nil :a)"] ["immutability" "true" "(let [m {:a 1} n (assoc m :b 2)] (and (= m {:a 1}) (= n {:a 1 :b 2})))"]) (defspec "map / collection keys (by value)" ["vector key literal" ":v" "(get {[1 2] :v} [1 2])"] ["map key literal" ":v" "(get {(hash-map :a 1) :v} {:a 1})"] ["assoc vector key" ":v" "(get (assoc {} [1 2] :v) [1 2])"] ["key across repr" ":v" "(get (assoc {} (vec [1 2]) :v) [1 2])"] ["frequencies of maps" "2" "(get (frequencies [{:a 1} (hash-map :a 1)]) {:a 1})"] ["group-by collection key" "1" "(count (group-by identity [{:a 1} (hash-map :a 1)]))"]) # Strictness: assoc bounds-checks vector indices; dissoc requires a map; # count rejects scalars; numerator/denominator have no ratio type. (defspec "map / strictness (throws like Clojure)" ["assoc vec out of bounds" :throws "(assoc [0 1 2] 4 4)"] ["assoc vec negative" :throws "(assoc [] -1 0)"] ["assoc vec at count ok" "[1 2 3]" "(assoc [1 2] 2 3)"] ["dissoc on number" :throws "(dissoc 42 :a)"] ["dissoc on vector" :throws "(dissoc [1 2] 0)"] ["dissoc on set" :throws "(dissoc #{:a} :a)"] ["dissoc nil ok" "nil" "(dissoc nil :a)"] ["count on number" :throws "(count 1)"] ["count on keyword" :throws "(count :a)"] ["count string ok" "3" "(count \"abc\")"] ["numerator throws" :throws "(numerator 1)"] ["denominator throws" :throws "(denominator 2)"] ["subvec out of range" :throws "(subvec [0 1 2 3] 1 5)"] ["subvec start>end" :throws "(subvec [0 1 2 3] 3 2)"] ["subvec ok" "[1 2]" "(subvec [0 1 2 3] 1 3)"] ["min-key empty" :throws "(apply min-key identity [])"] ["merge empty vector" :throws "(merge {} [])"] ["merge 1-elem vector" :throws "(merge {} [:foo])"] ["merge atomic arg" :throws "(merge {} :foo)"] ["merge [k v] ok" "{:foo 1}" "(merge {} [:foo 1])"] ["merge maps ok" "{:a 1, :b 2}" "(merge {:a 1} {:b 2})"])