How do I generate random keys for test.check? - unit-testing

I'm using test.check to generate a hashmap. I want one key to be a random numeric keywords This is what I tried
(gen/hash-map
:16 gen/nat
:1041 gen/string
(keyword (str gen/nat)) gen/string)
This works fine for the first two keys, but gen/sample returns a function for the last key instead of a value. An output from gen/sample {:16 1, :1041 "»", :clojure.test.check.generators.Generator#3daf97b7 "u"}

Here is an example using gen/bind
user> (require '[clojure.test.check.generators :as gen])
nil
user> (gen/sample (gen/bind gen/nat
#(gen/hash-map
:16 gen/nat
:1041 gen/string
(keyword (str %)) gen/string)))
({:16 0, :1041 "", :0 ""}
{:16 1, :1041 "—", :1 "Õ"}
{:16 1, :1041 "~", :2 "{"}
{:16 0, :1041 "", :0 ""}
{:16 1, :1041 "}", :2 "š¤#"}
{:16 4, :1041 "", :4 "ÂêÃ"}
{:16 6, :1041 "¯¾ùPêà", :1 "xaÝ$"}
{:16 1, :1041 "", :4 "ž"}
{:16 7, :1041 "", :3 "Ù¡"}
{:16 3, :1041 "‚«¤s", :5 "ƶoÒA“"})
user>
And here is one using gen/fmap
user> (gen/sample (gen/fmap (fn [[m n s]] (assoc m (keyword (str n)) s))
(gen/tuple (gen/hash-map
:16 gen/nat
:1041 gen/string)
gen/nat gen/string)))
({:16 0, :1041 "", :0 ""}
{:16 0, :1041 "", :1 "Ô"}
{:16 2, :1041 "`", :0 "V²"}
{:16 1, :1041 "X", :2 "b"}
{:16 3, :1041 "JÓ", :2 ""}
{:16 2, :1041 "", :2 ""}
{:16 2, :1041 "", :2 "×kîj"}
{:16 0, :1041 "", :0 ""}
{:16 5, :1041 "ÎÖ", :0 ":üv-`"}
{:16 8, :1041 "¬KéÎêo\r", :4 "ÆÌ"})
user>
In such cases, you have to use either gen/fmap or gen/bind. Both of them allow you to manipulate the output of a generator. If you want to return a generator, then use gen/bind. If you want to return a value, then use gen/fmap. Both gen/fmap and gen/bind take two arguments, one of which is a generator, and the other argument is a function that takes the value generated by the first as its input and returns either a generator (for gen/bind) or a value (for gen/fmap).

You'll need to use clojure.test.check.generators/fmap to apply your functions (str, keyword) to your generator.

Related

How to pretty print a Clojure data structure in HTML?

How to make the following code generate a similar visualization to pprint in HTML?
(let [clj-structure {:ui {:selected #{[:A 3]}}
:domain
{:C {0 {:value 12}}
:D {0 {:dependants [[:C 0]] :string "3" :value "3"}
1 {:dependants [[:C 0]] :string "4" :value "4"}}
:E {2 {:dependants [] :string "2" :value "2"}
3 {:dependants [] :string "10" :value "10"}}}}]
[:p (str clj-structure)])
(let [clj-structure {:ui {:selected #{[:A 3]}}
:domain
{:C {0 {:value 12}}
:D {0 {:dependants [[:C 0]] :string "3" :value "3"}
1 {:dependants [[:C 0]] :string "4" :value "4"}}
:E {2 {:dependants [] :string "2" :value "2"}
3 {:dependants [] :string "10" :value "10"}}}}]
[:pre (with-out-str (cljs.pprint/pprint clj-structure))])

Clojure how to mix/merge two maps

I'm new in clojure and I need mix a very complex (for me) maps that have inside a Vector to mix up.
Orginal map:
{:cars {:previous-page nil, :next-page 2, :count 33, :items [{:id 1, :name "test1"}, {:id 2, :name "test2"}]},
:trucks {:previous-page nil, :next-page 2, :count 11, :items [{:id 1, :name "test3"}, {:id 2, :name "test4"}]},
:boats {:previous-page nil, :next-page 2, :count 22, :items [{:id 1, :name "test5"}, {:id 2, :name "test6"}]}}
Second map:
{:cars {:previous-page 2, :next-page 3, :count 33, :items [{:id 3, :name "test7"}, {:id 4, :name "test8"}]},
:trucks {:previous-page 3, :next-page 4, :count 11, :items [{:id 3, :name "test9"}, {:id 4, :name "test10"}]},
:boats {:previous-page 4, :next-page 5, :count 22, :items [{:id 3, :name "test11"}, {:id 4, :name "test12"}]}}
I need to mix this two maps in only one:
{:cars {:previous-page 2, :next-page 3, :count 33, :items [{:id 1, :name "test1"}, {:id 2, :name "test2"},{:id 3, :name "test7"}, {:id 4, :name "test8"}]},
:trucks {:previous-page 3, :next-page 4, :count 11, :items [{:id 1, :name "test3"}, {:id 2, :name "test4"},{:id 3, :name "test9"}, {:id 4, :name "test10"}]},
:boats {:previous-page 4, :next-page 5, :count 22, :items [{:id 1, :name "test5"}, {:id 2, :name "test6"},{:id 3, :name "test11"}, {:id 4, :name "test12"}]}}
You can use merge-with to combine maps with an arbitrary function. Here there does not seem to be one "rule" though - for :previous-page, :next-page, and :count seem to be "last one wins" but :items seems to be something like#(into [] %)`. Once you can state that rule clearly as a function:
(fn combine [map1 map2] ...)
You can then easily combine the maps with merge-with using whatever combine you've defined.

Om Next Tutorial: Correctly calling the get-people function

I'm currently following along the om-next tutorial. In the Adding Reads section, there's a function get-people is defined. Along with this function, the init-data map is defined that contains a list of people.
(defn get-people [state key]
(let [st #state]
(into [] (map #(get-in st %)) (get st key))))
(def init-data {:list/one
[{:name "John", :points 0}
{:name "Mary", :points 0}
{:name "Bob", :points 0}],
:list/two
[{:name "Mary", :points 0, :age 27}
{:name "Gwen", :points 0}
{:name "Jeff", :points 0}]})
Here's my attempt to call this function.
(get-people (atom init-data) :list/one) ;; => [nil nil nil]
As you can see, I simply get back a vector of nils . I don't quite understand how I should call this function. Could somebody help me out? Thank you!
Alright, I figured it out.
init-data is not the correct data structure to call get-people with. The initial data must first be "reconciled" using Om's reconciler. You can find more information regarding the reconciler here in the tutorial's Normalization section.
Reconciling the init-data map and then derefing the data returns this normalized data structure:
{:list/one
[[:person/by-name "John"]
[:person/by-name "Mary"]
[:person/by-name "Bob"]],
:list/two
[[:person/by-name "Mary"]
[:person/by-name "Gwen"]
[:person/by-name "Jeff"]],
:person/by-name
{"John" {:name "John", :points 0},
"Mary" {:name "Mary", :points 0, :age 27},
"Bob" {:name "Bob", :points 0},
"Gwen" {:name "Gwen", :points 0},
"Jeff" {:name "Jeff", :points 0}}}
Here is a valid call to the get-people function using the reconciled init-data:
; reconciled initial data
(def reconciled-data
{:list/one
[[:person/by-name "John"]
[:person/by-name "Mary"]
[:person/by-name "Bob"]],
:list/two
[[:person/by-name "Mary"]
[:person/by-name "Gwen"]
[:person/by-name "Jeff"]],
:person/by-name
{"John" {:name "John", :points 0},
"Mary" {:name "Mary", :points 0, :age 27},
"Bob" {:name "Bob", :points 0},
"Gwen" {:name "Gwen", :points 0},
"Jeff" {:name "Jeff", :points 0}}}
; correct function call
(get-people (atom reconciled-data) :list/one)
; returned results
[{:name "John", :points 0}
{:name "Mary", :points 0, :age 27}
{:name "Bob", :points 0}]
Here's what's happening:
First, the function retrieves the value associated with the :list/one key. In this case, the value is a vector of paths into a map (each path is itself a vector).
Next, map over the paths and call the anonymous function on each vector. One of the calls would look like (get-in st [:person/by-name "John"]) and return {:name "John", :points 0}.
Return the results as a vector
If anybody is reading this and wants further clarification, please let me know.

replace nil values with zero in hash map

I have a hash map in clojure which contains some nil values. I am trying to group the data and sum the values, this gives me a null pointer due to the nil values. Can someone please advise on how I can iterate through the hash map and replace all nil values with integer 0?
(def data [{:MEDAL "SILVER" :EMEA 1 :NA nil :ASPAC 3}
{:MEDAL "GOLD" :EMEA 1 :NA 2 :ASPAC 3}
{:MEDAL "GOLD" :EMEA nil :NA 2 :ASPAC nil}
{:MEDAL "BRONZE" :EMEA nil :NA 2 :ASPAC 3}
{:MEDAL "SILVER" :EMEA 1 :NA 2 :ASPAC 3}
{:MEDAL "GOLD" :EMEA 1 :NA nil :ASPAC nil}
{:MEDAL "BRONZE" :EMEA 1 :NA 2 :ASPAC 3}])
Thanks
(map (fn [m]
(into {}
(map (fn [[k v]]
[k (if (nil? v) 0 v)]) m)))
data)
=> ({:EMEA 1, :NA 0, :MEDAL "SILVER", :ASPAC 3}
{:EMEA 1, :NA 2, :MEDAL "GOLD", :ASPAC 3}
{:EMEA 0, :NA 2, :MEDAL "GOLD", :ASPAC 0}
{:EMEA 0, :NA 2, :MEDAL "BRONZE", :ASPAC 3}
{:EMEA 1, :NA 2, :MEDAL "SILVER", :ASPAC 3}
{:EMEA 1, :NA 0, :MEDAL "GOLD", :ASPAC 0}
{:EMEA 1, :NA 2, :MEDAL "BRONZE", :ASPAC 3})
Rather than replace the nil values with zeros, you might consider just working with them by using keep. For example:
(apply + (keep :NA data))
; 10
(apply + (keep (fn [m] (when (= (:MEDAL m) "SILVER") (:EMEA m))) data))
; 2
You could use fnil.
(->> data
(apply merge-with conj (zipmap [:EMEA :NA :MEDAL :ASPAC] (repeat [])))
(reduce-kv #(assoc %1 %2 (reduce (fnil + 0 0) 0 %3)) {}))
Obviously :MEDAL needs other treatment.
I am assuming that you want to group the data by :MEDAL and then add EMEA, ASPAC etc., right?
;; Helper function
(defn batch-hashmaps
"Applies operation on values of keys in maps. filterf filters valid values.
Example: (batch-hashmaps + [:b :c] number? {:a 30, :b 50, :c :cheese} {:b 30, :c 40})
=> {:b 80, :c 40}"
[operation keys filterf & maps]
(apply conj {}
(for [key keys]
[key (apply operation (filter filterf (map #(key %) maps)))])))
(for [medal (set (map #(:MEDAL %) data))]
(assoc (apply (partial batch-hashmaps + [:EMEA :NA :ASPAC] number?) (filter #(= medal (:MEDAL %)) data) ) :MEDAL medal))
This should be the desired result:
({:MEDAL "GOLD", :EMEA 2, :NA 4, :ASPAC 3}
{:MEDAL "SILVER", :EMEA 2, :NA 2, :ASPAC 6}
{:MEDAL "BRONZE", :EMEA 1, :NA 4, :ASPAC 6})
And here is another quick and dirty thing.
(defn sum-medal
[medal data]
(assoc (apply (partial merge-with (fn [& [_ _ :as vals]] (apply + (filter number? vals)))) (filter #(= (:MEDAL %) medal) data)) :MEDAL medal))

Converting a vector of maps to map of maps in Clojure

I've a vector of maps like this:
[{:categoryid 1, :categoryname "foo" }
{:categoryid 2, :categoryname "bar" }
{:categoryid 3, :categoryname "baz" }]
and would like to generate a map of maps like this for searching by categoryname
{"foo" {:categoryid 1, :categoryname "foo" },
"bar" {:categoryid 2, :categoryname "bar" },
"baz" {:categoryid 3, :categoryname "baz" }}
How can I do this?
Another way: (into {} (map (juxt :categoryname identity) [...]))
(reduce (fn [m {catname :categoryname :as input}]
(assoc m catname input))
{}
[{:categoryid 1, :categoryname "foo" }
{:categoryid 2, :categoryname "bar" }
{:categoryid 3, :categoryname "baz" }])
Better yet,
(#(zipmap (map :categoryname %) %)
[{:categoryid 1, :categoryname "foo" }
{:categoryid 2, :categoryname "bar" }
{:categoryid 3, :categoryname "baz" }])
(ns code.groupby
(:use clojure.contrib.seq-utils))
(def vector-of-maps [ {:categoryid 1, :categoryname "foo" }
{:categoryid 2, :categoryname "bar" }
{:categoryid 3, :categoryname "baz" } ])
(group-by :categoryname vector-of-maps)
Gives you a map of Vectors of maps
{"bar" [{:categoryid 2, :categoryname "bar"}],
"baz" [{:categoryid 3, :categoryname "baz"}],
"foo" [{:categoryid 1, :categoryname "foo"}]}
(which I now realise isn't what you wanted...sorry)
I haven't tested it in Clojure, but in ClojureScript this variant appears to be faster than others:
(reduce #(assoc %1 (:categoryname %2) %2) {} [...])