Clojure Maps, order of key value creation - dictionary

In a clojure profiling library, there is a an atom storing some profiling information and it is modified by a number of functions. In the comments, they assert that the summary statistics (one of the functions) is called last for efficiency purposes, but I could not see how this was enforced in the actual code.
I then noticed that one manner in which this could enforced is the ordering in the construction of a map, so I recreated a simpler example to demonstrate.
(defn t []
(let [a (atom {})]
{:b (swap! a #(assoc % :data 'is-here))
:c (keys #a)}))
Here, in agreement with the comment, which one of :b or :c comes first in the code will determine the value of a. Of course there must be some ordering, but is this behaviour guaranteed? It seems like it should not be, since an unordered hash has no ordering, and this could have detrimental implications for parallelism.

I would consider the evaluation order of map literals an implementation detail and using non-constant expressions with side effects (e.g. setting an atom state) in map literals to be asking for trouble.
For well defined evaluation semantics, use a function call, where the arguments are guaranteed to be evaluated from left to right.
(let [i (atom 0)]
(hash-map :a (swap! i inc) :b (swap! i inc) :c (swap! i inc)))
;=> {:a 1, :c 3, :b 2}
Note that the keys are out of order since we used hash-map, but the values correspond to the keys in the order written.
Implementation details
The implementation details of map literals (in version 1.5) depends on several things:
The reader treats the unevaluated map literals as an ordered array-map for small maps (8 pairs or fewer) or an unordered hash-map for larger maps.
The compiler-evaluator parses the map expression provided by the reader as an unordered hash-map irrespective of size if the keys and values are constant expressions, but will evaluate it as an array-map. Otherwise, the compiler-evaluator evaluates in the key order provided by the reader, dependent on size.
An small example will make this a bit clearer (perhaps):
user=> {:a 1, :b 2, :c 3}
{:a 1, :c 3, :b 2}
user=> (type *1)
clojure.lang.PersistentArrayMap
user=> (def m1 {:a 1, :b 2, :c 3})
#'user/m1
user=> m1
{:a 1, :c 3, :b 2}
user=> (type m1)
clojure.lang.PersistentHashMap
user=> (eval m1)
{:a 1, :c 3, :b 2}
user=> (type *1)
clojure.lang.PersistentArrayMap
But...
user=> (def m2 {:a ((fn [] 1)), :b 2, :c 3})
#'user/m2
user=> m2
{:a 1, :b 2, :c 3}
user=> (type m2)
clojure.lang.PersistentArrayMap

I believe, as long as the {...} form remains equal to or less than 8 map entires (key/value pairs), the order will be maintained. A {...} will become a PersistentArrayMap with 8 or fewer entries, and a PersistentHashMap otherwise. The former will preserve the given order of its map entries, a guarantee not extended to the latter. Source.
As for parallelism, I think the following simple test shows that map entries will get evaluated in the order they are given, for maps created with 8 or fewer entries:
user=> (let [x (atom 0)]
{:a (do (Thread/sleep 1000) (swap! x inc))
:b (swap! x inc)})
{:a 1, :b 2}
But may evaluate in some other order for maps created with greater than 8 entries:
user=> (let [x (atom 0)]
{:a (do (Thread/sleep 1000) (swap! x inc))
:b (swap! x inc)
:c (swap! x inc)
:d (swap! x inc)
:e (swap! x inc)
:f (swap! x inc)
:g (swap! x inc)
:h (swap! x inc)
:i (swap! x inc)})
{:a 1, :c 2, :b 3, :f 4, :g 5, :d 6, :e 7, :i 8, :h 9}
Though, at least for hash-maps at 9 entries, it doesn't seem like any parallelism is going on during their instantation. That's interesting.

Related

Destructure and process vector of hash-maps in Clojure

I have a vector of hash-maps, like this:
(def my-maps [{:a 1} {:b 2}])
I want to loop over each hash-map, give the key and value a more meaningful name within the loop, then process each hash-map differently depending on its key.
Without further ado, here is my best attempt:
(for [m my-maps]
(let [my-key-name (key m) my-val-name (val m)]
(case my-key-name
:a (println "Found key :a with value " my-val-name)
:b (println "Found key :b with value " my-val-name))))
This approach, however, produces a rather cryptic error:
; Error printing return value (ClassCastException) at clojure.core/key (core.clj:1569).
; class clojure.lang.PersistentArrayMap cannot be cast to class java.util.Map$Entry (clojure.lang.PersistentArrayMap is in unnamed module of loader 'app'; java.util.Map$Entry is in module java.base of loader 'bootstrap')
What am I doing wrong?
You can destructure inside for (or use doseq):
(for [[[k v] & _] [{:a 1} {:b 2}]]
(println "Found key" k "with value" v))
Found key :a with value 1
Found key :b with value 2
=> (nil nil)
For the sake of clarity, here is a more general answer broken down into individual steps:
(let [my-maps [{:a 1} {:b 2 :c 3}]]
(doseq [curr-map my-maps]
(newline)
(println "curr-map=" curr-map)
(let [map-entries (seq curr-map)]
(println "map-entries=" map-entries)
(doseq [curr-me map-entries]
(let [[k v] curr-me]
(println " curr-me=" curr-me " k=" k " v=" v))))))
With result
curr-map= {:a 1}
map-entries= ([:a 1])
curr-me= [:a 1] k= :a v= 1
curr-map= {:b 2, :c 3}
map-entries= ([:b 2] [:c 3])
curr-me= [:b 2] k= :b v= 2
curr-me= [:c 3] k= :c v= 3
A MapEntry object in Clojure can be treated as either a 2-element vector (accessed via first & second) or as a MapEntry accessed via the key and val functions. The destructuring form:
(let [[k v] curr-me]
treats the MapEntry object curr-me as a sequence and pulls out the first 2 elements into k and v. Even though it prints like a vector (eg [:a 1]), it does have the type clojure.lang.MapEntry.
The destructuring syntax & _ in the for expression of the original answer is a "rest args" destructuring. It causes the sequence of all MapEntry objects after the first one to be assigned to the variable _, which is then ignored in the rest of the code.

How to return all keywords in a map

I want to return all the keywords appeared in map. For instance:
{:a 1 :d 4 :e 4}
I want to get (:a :d :e). my code is
(get {:a 1 :d 4 :e 4} :keywords)
It returns nil. So how to fix it?
I guess you want to get all the keys from your map. You can do that using:
(keys {:a 1, :d 4, :e 4}
;; => (:a :d :e)
If you would like to get all the keywords when they appear as keys in a map you need to filter only those matching keyword? predicate:
(filter keyword? (keys {:a 1, 'd 4, :e 4, "f" 5}))
;; => (:a :e)
Similarly for keywords from map values:
(filter keyword? (vals {:a :b, "c" :d, 4 "e", 5 'f}))
;; => (:b :d)

Is there an idiomatic way to find matching key and value in map in Clojure?

I'm trying to find matching key and value pairs from a map. I'm using the following code:
(defn matches? [m k v]
(let [val (k m)]
(= val v)))
my-demo.core=> (matches? {:a 1 :b 2} :b 2)
true
my-demo.core=> (matches? {:a 1 :b 2} :b 3)
false
Another approach using superset?:
my-demo.core=> (superset? #{:a 1 :b 3} #{:a 1})
true
my-demo.core=> (superset? #{:a 1 :b 3} #{:a 2})
false
I have a feeling there is a better way to do this.
My question is: Is there an idiomatic way to find matching key and value in map in Clojure?
This is probably a small enough problem that you could just use this instead of defining a function:
(= ({:a 1 :b 2} :a)
1)
=> true
I would say this is an idiomatic way, which will work fine for most use cases.
However, it depends on the behaviour you require when testing for a nil value. because the above method would return true for :c nil:
(= ({:a 1 :b 2} :c)
nil)
=> true
And your function behaves the same way:
(matches? {:a 1 :b 2} :c nil)
=> true
To get around this you could use get with a "not found" value:
(= (get {:a 1 :b 2} :c ::not-found)
nil)
=> false
This works fine but it's perhaps not as neat. You'd just have to make sure that your "not found" value was never the same as your test value.
If you wanted to really know that a map contains a key with a possibly nil value you would instead have to check both things. Here's a function that would do this while only doing the hash-map lookup once. It uses (find map key) which returns the map entry (the key-value pair) for key, or nil if the key is not present.
(defn contains-kv? [m k v]
(if-let [kv (find m k)]
(= (val kv) v)
false))
(contains-kv? {:a 1 :b nil} :a 1)
=> true
(contains-kv? {:a 1 :b nil} :b nil)
=> true
(contains-kv? {:a 1 :b nil} :c nil)
=> false
Note: I don't think superset? is doing what you think it does. In that example you're using sets, not hash maps, which are completely different:
(clojure.set/superset? #{:a 1 :b 2} #{:a :b})
=> true
Your matches? function looks good to me, though I'd probably remove the let in this case, as it removes a bit of clutter. I'd also rename it to something more precise, though this is the best I can come up with just now:
(defn contains-kv?
"Returns true if the key k is present in the given map m and it's value matches v."
[m k v]
(= (m k) v))

Clojure: applyng function on every value in a map in vector of maps

How to apply a function on every value in a map in vector of maps.
If I have a vector of maps
(def vector-of-maps [{:a 1 :b 2} {:a 3 :b 4}])
And want to apply a function on every value in every map and as a result I want the same vector of maps, something like this
(map #(+ 1 %) vector-of-maps)
So that the result is
[{:a 2 :b 3} {:a 4 :b 5}]
And I want it to work with any vector of maps, not just this particular one....
=> (defn update-map [m f] (reduce-kv (fn [m k v] (assoc m k (f v))) {} m))
=> (map #(update-map % inc) vector-of-maps)
({:b 3, :a 2} {:b 5, :a 4})
Perhaps
(defn mapv-map-values [f vm]
(letfn [(map-values [m] (zipmap (keys m) (map f (vals m))))]
(mapv map-values vm)))
... producing
(mapv-map-values inc [{:a 1 :b 2} {:a 3 :b 4}])
;[{:b 3, :a 2} {:b 5, :a 4}]
The map-values function, the only significant departure from #user100464's answer, was adapted from here.
also you can do like this,but it is ugly
(defn dodo [m] (map (fn [map] (apply merge (for [[k v] map] (assoc {} k (inc v))))) m))
(dodo [{:a 1 :b 2} {:a 3 :b 4}])
;({:a 2, :b 3} {:a 4, :b 5})

Maps and records equality in Clojure

I found the behavior of Clojure confusing regarding equality between maps and records. In this first example, we have two different types which are structurally equal. The equality = function returns true:
user> (defn make-one-map
[]
{:a "a" :b "b"})
#'user/make-one-map
user> (def m1 (make-one-map))
#'user/m1
user> m1
{:a "a", :b "b"}
user> (def m2 {:a "a" :b "b"})
#'user/m2
user> m2
{:a "a", :b "b"}
user> (= m1 m2)
true
user> (type m1)
clojure.lang.PersistentArrayMap
user> (type m2)
clojure.lang.PersistentHashMap
In the second example we have a hashmap and a record which are structurally equivalent but the = function returns false:
user> (defrecord Titi [a b])
user.Titi
user> (def titi (Titi. 1 2))
#'user/titi
user> titi
#user.Titi{:a 1, :b 2}
user> (= titi {:a 1 :b 2})
false
Why are the differences? I'm using Clojure 1.3 and I found them really confusing.
From the docstring for defrecord:
In addition, defrecord will define type-and-value-based =, and will
defined Java .hashCode and .equals consistent with the contract for
java.util.Map.
So, when using =, type is taken into account. You could use .equals instead:
user> (.equals titi {:a 1 :b 2})
true
a PersistentArrayMap and a PersistentHashMap are conceptually the same - as the ArrayMap grows, it will automatically get converted to a HashMap for performance reasons. User-level code should generally not try to distinguish between the two.
A defrecord datatype, on the other hand, is not the same as one of the other maps. It is a separate type that may implement entirely different interfaces and should not be automatically replaced by some other form of map. It is not conceptually equal to a normal map, so = returns false.

Resources