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.
Related
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.
I want to convert {a 1, b 2} clojure.lang.PersistentArrayMap into
[a 1 b 2] clojure.lang.PersistentVector in clojure.
I have tried to write a function in clojure which converts {a 1, b 2} into [a 1 b 2]. I have also written a macro which gives me expected end result. In clojure we cannot pass the values generated inside functions to macros. For that I wanted to know a way in which I can implement a macro directly which can convert {a 1, b 2} into (let [a 1 b 2] (println a)), which will return 1.
Dummy Macro:
(defmacro mymacro [binding & body]
--some implemetation---)
Execution :
(mymacro '{a 1, b 2} (println a))
output:
1
nil
My Implementaion:
Function which converts into desired output.
(defn myfn [x]
(let [a (into (vector) x) b (vec (mapcat vec a))] b))
Execution:
(myfn '{a 1, b 2})
Output:
[a 1 b 2]
MACRO:
(defmacro list-let [bindings & body] `(let ~(vec bindings) ~#body))
Execution:
(list-let [a 1 b 2] (println a))
Output:
1
nil
I wanted to know how can I implement the same inside the macro itself and avoid the function implementation to get the require output. Something same as dummy macro given above. I am also interested in knowing if there is any which through which I can pass the value from my funtion to the macro without using
(def)
in general, macro code is plain clojure code (with the difference that it returns clojure code to be evaluated later). So, almost anything you can think of coding in clojure, you can do inside macro to the arguments passed.
for example, here is the thing you're trying to do (if i understood you correctly):
(defmacro map-let [binding-map & body]
(let [b-vec (reduce into [] binding-map)]
`(let ~b-vec ~#body)))
(map-let {a 10 b 20}
(println a b)
(+ a b))
;;=> 10 20
30
or like this:
(defmacro map-let [binding-map & body]
`(let ~(reduce into [] binding-map) ~#body))
or even like this:
(defmacro map-let [binding-map & body]
`(let [~#(apply concat binding-map)] ~#body))
You don't need a macro for this, and you should always prefer functions over macros when possible.
For your particular case, I have already written a function keyvals which you may find handy:
(keyvals m)
"For any map m, returns the keys & values of m as a vector,
suitable for reconstructing via (apply hash-map (keyvals m))."
(keyvals {:a 1 :b 2})
;=> [:b 2 :a 1]
(apply hash-map (keyvals {:a 1 :b 2}))
;=> {:b 2, :a 1}
And, here are the full API docs.
If you are curious about the implementation, it is very simple:
(s/defn keyvals :- [s/Any]
"For any map m, returns the (alternating) keys & values of m as a vector, suitable for reconstructing m via
(apply hash-map (keyvals m)). (keyvals {:a 1 :b 2} => [:a 1 :b 2] "
[m :- tsk/Map ]
(reduce into [] (seq m)))
(def m {:a 1 :b 2 :c 3})
Let's say I want each value in m to be incremented. The only way I can think of to do that is
(into {}
(map
(fn [[key val]]
[key (inc val)])
m))
Is there a better way to do this? I need to do this a lot in my code and it looks kind of hacky. I really do need to use a map here (mostly for O(1) lookups, the key will be a UUID and the value a map), not a vector or a list.
Found something that looks good here: http://clojuredocs.org/clojure.core/reduce-kv.
(defn update-map [m f]
(reduce-kv (fn [m k v]
(assoc m k (f v))) {} m))
Then you can do
(update-map {:a 1 :b 2} inc)
to get
{:a 2 :b 3}
If needed you can supply k to f or make a update-key-values function that takes in two functions f and g and applies them to the keys and values respectively.
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))
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.