Is there a short form for creating hash-map in Clojure? - clojure

Is there a short form/macro that allows me to do
(defn f [a b c]
{a b c})
instead of
(defn f [a b c]
{:a a :b b :c c})

(defmacro as-map [& syms]
(zipmap (map keyword syms) syms))
Usage:
(def a 42)
(def b :foo)
(as-map a b)
;;-> {:a 42 :b :foo}
Note that to support namespaced keywords, you'd have to drop support for ns aliases if you want to keep it as short:
(defmacro as-map [& syms]
(zipmap (map keyword syms) (map (comp symbol name) syms)))
Usage:
(def a 42)
(def b :foo)
(as-map example/a foo-of/b)
;;-> {:example/a 42 :foo-of/b :foo}
Advice: Likely not a good idea, saves you a few keyboard hits at the cost of readability and expressivity and flexibility in naming local bindings.

This shows the steps. Remove the println's for actual use:
(ns clj.core
(:gen-class))
(defmacro hasher [& args]
(let [keywords (map keyword args)
values args
keyvals-list (interleave keywords values)
]
(println "keywords " keywords)
(println "values " values)
(println "keyvals-list " keyvals-list)
`(hash-map ~#keyvals-list)
)
)
(def a 1)
(def b 2)
(println \newline "result: " (hasher a b))
> lein run
keywords (:a :b)
values (a b)
keyvals-list (:a a :b b)
result: {:b 2, :a 1}

This is an old snippet of mine I've had kicking around for a while.
(declare ^:private restructure*)
(defn ^:private restructure-1 [m [e k]]
(cond
(= :strs e) (reduce #(assoc %1 (name %2) %2) m k)
(= :keys e) (reduce #(assoc %1 (keyword (namespace %2) (name %2)) %2) m k)
:else (assoc m k (restructure* e))))
(defn ^:private restructure* [form]
(if-not (map? form)
form
(as-> {} v
(reduce restructure-1 v form)
`(hash-map ~#(mapcat identity v)))))
(defmacro restructure [form]
(restructure* form))
The idea is that it provides the complement of clojure.core/destructure which goes from a destructuring form to bindings, this captures bindings and constructs a datastructure.
(let [x 1 y 2 z 3]
(restructure {:keys [x y z]}))
;; => {:x 1 :y 2 :z 3}

Related

Pretty-print Nested Hash-map in ClojureScript

Is there a convenient way in ClojureScript to pretty print a nested hash-map in the way that the whole tree-structure becomes immediately visible.
For instance a map like this
(def my-map {:a {:b 1 :c 9} :b {:d 8 :e {:f 2 :g 3 :h 4}} :c 10})
should be printed like this:
{:a {:b 1
:c 9}
:b {:d 8
:e {:f 2
:g 3
:h 4}}
:c 10}
EDIT: There might also be vectors in the map. The usecase is just to inspect larger data structures during development.
There is no built-in way to do it. You might come close to what you want by using cljs.pprint and setting cljs.pprint/*print-right-margin* to a low value.
I would recommend to take a look at a small library shodan which provides a very useful inspect function:
(require '[shodan.inspection :refer [inspect]])
(inspect {:aaaaaa 1
:bbbbbb {:ccc 2
:dddddd [1 2 3 4 5]}})
It won't print anything in your CLJS REPL but will provide a handy view in your browser's console:
You can collapse and expand nested datastructures - it basically does what you asked for.
As a personal challenge I wrote the following code:
(enable-console-print!)
(def atomic? (complement coll?))
(def padding #(apply str (repeat % " ")))
(def tabulate #(apply str (repeat % "\t")))
(def strcat #(->> (apply concat %&) (apply str)))
(defn my-max-key [x] (if (empty? x) [""] (apply (partial max-key count) x)))
(defn longest-key [m] (->> m keys (filter atomic?) (map str) my-max-key))
(def length (comp count str))
(def not-map? (complement map?))
(def nested? #(some coll? %))
(def join #(apply str (interpose % %2)))
(def join-lines (partial join "\n"))
(defn has-atomic? [coll] (some atomic? coll))
(defn diff-key-lengths [key1 key2] (- (length key1) (length key2)))
(defn convert
([thing] (convert -1 thing))
([depth thing]
(defn convert-items []
(defn convert-seq []
(conj []
(map (partial convert (inc depth)) thing)
""))
(defn string-horizontally [[key value]]
(str (tabulate (inc depth))
key
(padding (diff-key-lengths (longest-key thing) key))
" → "
value))
(defn string-vertically [[key value]]
(str (convert (inc depth) key) "\n"
(convert (+ 2 depth) "↓") "\n"
(convert (inc depth) value) "\n"))
(defn convert-kv [[key value]]
(if (nested? [key value])
(string-vertically [key value])
(string-horizontally [key value])))
(cond (atomic? thing)
[(str (tabulate depth) thing)]
(not-map? thing)
(convert-seq)
(map? thing)
(map convert-kv thing)))
(->> (convert-items) flatten join-lines)))
(def sample-input [["the first thing in this nested vector"]
{{"this is a key in a nested map"
"that points to me!!!"}
{"and that entire map points to this map!!!"
"cool!!!"
"but it gets cooler cause..."
"the value's line up!!!"}}])
(->> sample-input convert println)
The terminal output is (psst... the values in a map do line up but I don't think that chrome uses a monospaced font!):

Find Value of Specific Key in Nested Map

In Clojure, how can I find the value of a key that may be deep in a nested map structure? For example:
(def m {:a {:b "b"
:c "c"
:d {:e "e"
:f "f"}}})
(find-nested m :f)
=> "f"
Clojure offers tree-seq to do a depth-first traversal of any value. This will simplify the logic needed to find your nested key:
(defn find-nested
[m k]
(->> (tree-seq map? vals m)
(filter map?)
(some k)))
(find-nested {:a {:b {:c 1}, :d 2}} :c)
;; => 1
Also, finding all matches becomes a matter of replacing some with keep:
(defn find-all-nested
[m k]
(->> (tree-seq map? vals m)
(filter map?)
(keep k)))
(find-all-nested {:a {:b {:c 1}, :c 2}} :c)
;; => [2 1]
Note that maps with nil values might require some special treatment.
Update: If you look at the code above, you can see that k can actually be a function which offers a lot more possibilities:
to find a string key:
(find-nested m #(get % "k"))
to find multiple keys:
(find-nested m #(some % [:a :b]))
to find only positive values in maps of integers:
(find-nested m #(when (some-> % :k pos?) (:k %)))
If you know the nested path then use get-in.
=> (get-in m [:a :d :f])
=> "f"
See here for details: https://clojuredocs.org/clojure.core/get-in
If you don't know the path in your nested structure you could write a function that recurses through the nested map looking for the particular key in question and either returns its value when it finds the first one or returns all the values for :f in a seq.
If you know the "path", consider using get-in:
(get-in m [:a :d :f]) ; => "f"
If the "path" is unknown you can use something like next function:
(defn find-in [m k]
(if (map? m)
(let [v (m k)]
(->> m
vals
(map #(find-in % k)) ; Search in "child" maps
(cons v) ; Add result from current level
(filter (complement nil?))
first))))
(find-in m :f) ; "f"
(find-in m :d) ; {:e "e", :f "f"}
Note: given function will find only the first occurrence.
Here is a version that will find the key without knowing the path to it. If there are multiple matching keys, only one will be returned:
(defn find-key [m k]
(loop [m' m]
(when (seq m')
(if-let [v (get m' k)]
v
(recur (reduce merge
(map (fn [[_ v]]
(when (map? v) v))
m')))))))
If you require all values you can use:
(defn merge-map-vals [m]
(reduce (partial merge-with vector)
(map (fn [[_ v]]
(when (map? v) v))
m)))
(defn find-key [m k]
(flatten
(nfirst
(drop-while first
(iterate (fn [[m' acc]]
(if (seq m')
(if-let [v (get m' k)]
[(merge-map-vals m') (conj acc v)]
[(merge-map-vals m') acc])
[nil acc]))
[m []])))))

Define clojure hash map without duplicating tokens

I'd like to return a hash-map like so:
(fn [foo bar] {:foo foo :bar bar})
Is it possible to do that without repeating the names? Something like how let allows this:
(let [{:keys [foo bar]} args]
(...))
The macro:
(defmacro some-hash-thing [& vals]
(zipmap (map keyword vals) vals))
And in use:
(let [a 4, b 5]
(some-hash-thing b a))
;; => {:a 4, :b 5}
(defmacro as-keymap [& names] `(conj {} ~#(map (juxt keyword symbol) names)))

Clojure Multi Maps

Very simple + silly question:
Does clojure provide multi maps? I currently have something like this:
(defn wrap [func]
(fn [mp x]
(let [k (func x)]
(assoc mp k
(match (get mp k)
nil [x]
v (cons v x))))))
(defn create-mm [func lst]
(reduce (wrap func) {} lst))
Which ends up creating a map, where for each key, we have a vector of all elements with that key. However, it seems like multi maps is a very basic data structure, and I wonder if clojure has it built-in.
Thanks
I don't think this is really necessary as a distinct type, as Clojure's flexibility allow you to quickly make your own by just using maps and sets. See here:
http://paste.lisp.org/display/89840
Edit (I should have just pasted this in since it's so small)
Example Code (Courtesy Stuart Sierra)
(ns #^{:doc "A multimap is a map that permits multiple values for each
key. In Clojure we can represent a multimap as a map with sets as
values."}
multimap
(:use [clojure.set :only (union)]))
(defn add
"Adds key-value pairs the multimap."
([mm k v]
(assoc mm k (conj (get mm k #{}) v)))
([mm k v & kvs]
(apply add (add mm k v) kvs)))
(defn del
"Removes key-value pairs from the multimap."
([mm k v]
(let [mmv (disj (get mm k) v)]
(if (seq mmv)
(assoc mm k mmv)
(dissoc mm k))))
([mm k v & kvs]
(apply del (del mm k v) kvs)))
(defn mm-merge
"Merges the multimaps, taking the union of values."
[& mms]
(apply (partial merge-with union) mms))
(comment
(def mm (add {} :foo 1 :foo 2 :foo 3))
;; mm == {:foo #{1 2 3}}
(mm-merge mm (add {} :foo 4 :bar 2))
;;=> {:bar #{2}, :foo #{1 2 3 4}}
(del mm :foo 2)
;;=> {:foo #{1 3}}
)
Extra test for the case pointed out in the comments:
(comment
(-> {} (add :a 1) (del :a 1) (contains? :a))
;;=> false
)

How to set default values for fields in records in Clojure?

I am creating records in Clojure and would like to set some fields up with a default value. How can I do this?
Use a constructor function.
(defrecord Foo [a b c])
(defn make-foo
[& {:keys [a b c] :or {a 5 c 7}}]
(Foo. a b c))
(make-foo :b 6)
(make-foo :b 6 :a 8)
Of course there are various variations. You could for example require certain fields to be non-optional and without a default.
(defn make-foo
[b & {:keys [a c] :or {a 5 c 7}}]
(Foo. a b c))
(make-foo 6)
(make-foo 6 :a 8)
YMMV.
You can pass initial values to a record pretty easily when you construct it though an extension map:
(defrecord Foo [])
(def foo (Foo. nil {:bar 1 :baz 2}))
In light of this, I usually create a constructor function that merges in some default values (which you can override as you want):
(defn make-foo [values-map]
(let [default-values {:bar 1 :baz 2}]
(Foo. nil (merge default-values values-map))))
(make-foo {:fiz 3 :bar 8})
=> #:user.Foo{:fiz 3, :bar 8, :baz 2}
After having the same question, I ended up wrapping the defrecord and the factory function up into a single definition using a macro.
The macro:
(defmacro make-model
[name args & body]
(let [defaults (if (map? (first body)) (first body) {})
constructor-name (str/lower-case (str "make-" name))]
`(do (defrecord ~name ~args ~#(if (map? (first body)) (rest body) body))
(defn ~(symbol constructor-name)
([] (~(symbol constructor-name) {}))
([values#] (~(symbol (str "map->" name)) (merge ~defaults values#)))))))
Usage
(make-model User [firstName lastName] {:lastName "Smith"})
=> #'user/make-user
(make-user {:firstName "John"})
=> #user.User{:firstName "John", :lastName "Smith"}