I'm just starting to learn clojure and have been reading some simple examples and then doing my best to rtfm for concepts.
However I'm a bit confused by what val is doing in the example below. This has been taken from the Clojure doc examples for val.
(first {:one :two}) ;; => [:one :two]
Here, a hash-map with a key of :one and a value of :two is being passed to first. Behind the scenes, Clojure converts this hash-map to a sequence of vectors. Since there is only one vector in this sequence, it returns [:one :two].
(val (first {:one :two})) ;; => :two
(val [:one :two]) ;; => ClassCastException clojure.lang.PersistentVector cannot be cast to java.util.Map$Entry
(val {:one :two}) ;; => ClassCastException clojure.lang.PersistentArrayMap cannot be cast to java.util.Map$Entry
If I try to call val on a (I think) a hash-map (I realize it's actually a "persistent array map"), I get the exception as seen above.
I'm also confused by the following:
(first {:one :two}) ;; # => [:one :two] (this is a vector right?)
(val [:one :two]) ;; # => ClassCastException (why doesn't this give back the same result as the example above?)
Why can't I just plug the result of (first {:one :two}) into val and get the same result?
Additionally, another example listed on the page is the following:
(map val {:a 1 :b 2}) ;; => (1 2)
Here's how I read the line. Take the array-map {:a 1 :b 2}. For each key-value pair, call val on the pair to return the value. Return a sequence from the resulting calls to map. Is this the correct way to read the problem?
As always, thanks for any and all help.
a sequence of a map produces MapEntry values as you've noted, which look like and can be compared with vectors
user=> (= (first {:a 1 :b 2}) [:a 1])
true
but aren't the same class
user=> (= (class (first {:a 1 :b 2})) (class [:a 1]))
false
So although the output on the repl of (first {:a 1}) looks like a vector, it isn't, it's a MapEntry, so it can be passed to val, but the vector [:a 1] cannot, hence the class cast exception.
Your reading of what map is doing is correct at a high level, a little more specific might be "For each entry in the sequence from {:a 1 :b 2} (which are MapEntry values) call the function val on each item (a MapEntry), and generate a sequence from the results".
This will explain why something like
user=> (map val '([:a 1] [:b 2]))
will cause the same ClassCastExceptions as the sequence generates Vector elements, not MapEntry elements.
val returns value of a map entry, not a map.
(first {:one :two}) return the first map entry (although it appears to be just a vec)
(map val {:one :two}) return the value of every entry, and is equivalent to (vals {:one :two})
(first {:one :two}) ;; # => [:one :two] (this is a vector right? No, it's not.)
[:one :two] in this case is a MapEntry, not a vector.
Related
Is there a way to use a single list comp to achieve the following or do I need some other method?
(def args '([[:db/foo 1 2] [:db/bar 3 4]] {:a 1 :b 2}))
(defn myfunc []
"Want to run inner loop only once if arg is a map."
(for [arg args [cmd & params] arg] ; 'cmd & params' are meaningless if map.
(if (map? arg)
arg
(into [cmd] params))))
Above code produces
=> [[:db/foo 1 2] [:db/bar 3 4] {:a 1, :b 2} {:a 1, :b 2}]
But I actually want
=> [[:db/foo 1 2] [:db/bar 3 4] {:a 1, :b 2}]
Obviously this isn't the full function, I'm doing transforms if the arg is in vector form but want to let map form pass straight through (without being duplicated).
Update: I've found that a list comprehension has its roots in set builder notation, whereas 'conditionally executing the inner loop' is actually an imperative notion, so it's not surprising this isn't easy to express with a single list comprehension, which is a totally fp construct.
Is this what you want to do?:
Build a new sequence from an existing sequence. For every element in the input sequence, there are two possible cases:
It is a map: In that case, just append it to the result sequence
Otherwise it is a sequence and its elements should possibly be transformed and appended to the result sequence
If this is what you want to do, this computation can conveniently be expressed by first mapping the elements of the input sequence to subsequences and then concatenating those sequences to form the resulting sequence.
As suggested by #cfrick, there is mapcat that combines mapping with concatenation, either by passing in a sequence directly to mapcat
(mapcat (fn [x] (if (map? x) [x] x)) args)
or by using mapcat as a transducer
(into [] (mapcat (fn [x] (if (map? x) [x] x))) args)
I don't think for is suitable for expressing this computation.
before I propose a solution, I have a few remarks on the function myfunc that you provided:
the doc string is misplaced. I know it's weird but the doc string must be before the arguments of a function, so before [],
your usage of for instruction is not what you expect I think. You should take a look on the example in the documentation here. Mainly the for instruction is nice to construct Cartesian product from list.
I hope that I understood correctly what you want to achieve. In my proposition, I use a recursion through loop instruction to build a vector which will contain every arguments:
(defn myfunc "Want to run inner loop only once if arg is a map."
[& args]
(loop [arg-to-parse args
res []]
(if (empty? arg-to-parse)
res
(let [arg (first arg-to-parse)
new-arg-to-parse (rest arg-to-parse)]
(if (map? arg)
(recur new-arg-to-parse (conj res arg))
(recur new-arg-to-parse (into res arg))
)))))
(myfunc [[:db/foo 1 2] [:db/bar 3 4]] {:a 1 :b 2})
;; => [[:db/foo 1 2] [:db/bar 3 4] {:a 1, :b 2}]
(myfunc {:a 1 :b 2} [[:db/foo 1 2] [:db/bar 3 4]])
;; => [{:a 1, :b 2} [:db/foo 1 2] [:db/bar 3 4]]
I am new to Clojure. I am trying to use java hashmap in clojure. I am passing a java hashmap to Clojure. The map is- {0=Goa, 1=Delhi, 2=Mumbai}. When I am trying to use the clojure functions on this map I am not getting the expected output. In contrast to this when I am iterating over this map it is giving the expected output.
Example
(println(get map 0)) is giving nil
(doseq [[key value] map
(println value)) is giving the expected output.
Output-Goa
Delhi
Mumbai
Can somebody please expain me why is this happening?
You really should google a bit to find pre-existing answers like this one: Clojure: working with a java.util.HashMap in an idiomatic Clojure fashion
You can then see a simple answer:
(def data {:a 1 :b 2 :c 3})
(def java-map (java.util.HashMap. data))
(def clj-map (into {} java-map))
which gives us:
java-map => <#java.util.HashMap {:b 2, :c 3, :a 1}>
clj-map => <#clojure.lang.PersistentArrayMap {:b 2, :c 3, :a 1}>
and looping:
(doseq [[k v] clj-map]
(println (format "key=%s val=%s" k v)) )
with result:
key=:b val=2
key=:c val=3
key=:a val=1
I think your problem is that your map is named "map" which is also a Clojure function. Try it like this:
(def my-map {0 "Goa" 1 "Delhi" 2 "Mumbai"})
Which then will work like this:
(println (get my-map 0))
Note that it still returns nil, since there is nothing else after the (println) form but it does print the value of the 0 in the map, which is "Goa".
(def input-map {0 "Goa" 1 "Delhi" 2 "Mumbai"})
(map (fn[[k v]] (print "key " k " value " k)) input-map)
[[k v]] for function let you access key and value for each entry
(map print input-map)
here map entry will be passed as parameter to print
This makes sense:
user=> (into {} [[:a 1] [:b 2]])
{:a 1, :b 2}
But why does this generate an error?
user=> (into {} (partition 2 [:a 1 :b 2]))
ClassCastException clojure.lang.Keyword cannot be cast to java.util.Map$Entry clojure.lang.ATransientMap.conj (ATransientMap.java:44)
Just to be sure:
user=> (partition 2 [:a 1 :b 2])
((:a 1) (:b 2))
Does into have a problem with lazy sequences? If so, why?
Beyond an explanation of why this doesn't work, what is the recommended way to conj a sequence of key-value pairs like [:a 1 :b 2] into a map? (apply conj doesn't seem to work, either.)
You can apply the sequence to assoc:
(apply assoc {:foo 1} [:a 1 :b 2])
=> {:foo 1, :a 1, :b 2}
Does into have a problem with lazy sequences? If so, why?
No, into is commonly used with lazily evaluated sequences. This is lazy, but each key/value tuple is a vector, which is why it works when into is reducing the pairs into the map:
(into {} (map vector (range 3) (repeat :x)))
=> {0 :x, 1 :x, 2 :x}
This doesn't work because the key/value pairs are lists:
(into {} (map list (range 3) (repeat :x)))
So the difference isn't laziness; it's due to into using reduce using conj on the map, which only works with vector key/value pairs (or MapEntrys):
(conj {} [:a 1]) ;; ok
(conj {} (MapEntry. :a 1)) ;; ok
(conj {} '(:a 1)) ;; not ok
Update: assoc wrapper for applying empty/nil sequences as suggested in comments:
(defn assoc*
([m] m)
([m k v & kvs]
(apply assoc m k v kvs)))
The recommended way – (assuming the seq arg is non-empty, as pointed out by the OP) – would be
Clojure 1.9.0
user=> (apply assoc {} [:a 1 :b 2])
{:a 1, :b 2}
The version with partition doesn't work because the blocks that partition returns are seqs and those are not treated as map entries when conj'd on to a map the way vectors and actual map entries are.
E.g. (into {} (map vec) (partition 2 [:a 1 :b 2])) would work because here the pairs get converted to vectors before conjing.
Still the approach with assoc is preferable unless there's some particular circumstance that makes into convenient (like, say, if you have a bunch of transducers that you want to use for preprocessing your partition-generated pairs etc.).
Clojure treats a 2-vec such as [:a 1] as equivalent to a MapEntry, doing what amounts to "automatic type conversion". I try to avoid this and always be explicit.
(first {:a 1}) => <#clojure.lang.MapEntry [:a 1]>
(conj {:a 1} [:b 2]) => <#clojure.lang.PersistentArrayMap {:a 1, :b 2}>
So we see that a MapEntry prints like a vector but has a different type (just like a Clojure seq prints like a list but has a different type). seq converts a Clojure map into a sequence of MapEntry's, and first gets us the first one (most Clojure functions call (seq ...) on any input collections before any other processing).
Notice that conj does the inverse type conversion, treating the vector [:b 2] as if it were a MapEntry. However, conj won't perform automatic type conversion for a list or a seq:
(throws? (conj {:a 1} '(:b 2)))
(throws? (into {:a 1} '(:b 2)))
into has the same problem since it is basically just (reduce conj <1st-arg> <2nd-seq>).
The other answers already have 3 ways that work:
(assoc {} :b 2) => {:b 2}
(conj {} [:b 2]) => {:b 2}
(into {} [[:a 1] [:b 2]]) => {:a 1, :b 2}
However, I would avoid those and stick to either hash-map or sorted-map, both of which avoid the problem of empty input seqs:
(apply hash-map []) => {} ; works for empty input seq
(apply hash-map [:a 1 :b 2]) => {:b 2, :a 1}
If your input sequence is a list of pairs, flatten is sometimes helpful:
(apply sorted-map (flatten [[:a 1] [:b 2]])) => {:a 1, :b 2}
(apply hash-map (flatten '((:a 1) (:b 2)))) => {:a 1, :b 2}
P.S.
Please be note that these are not the same:
java.util.Map$Entry (listed in jdk docs as "Map.Entry")
clojure.lang.MapEntry
P.P.S
If you already have a map and want to merge in a (possibly empty) sequence of key-value pairs, just use a combination of into and hash-map:
(into {:a 1} (apply hash-map [])) => {:a 1}
(into {:a 1} (apply hash-map [:b 2])) => {:a 1, :b 2}
Given
[["foo" "bar" 2] ["biz" "baf" 3]]
how do I get
[{:a "foo" :b "bar" :num 2} {:a "biz" :b "baf" :num 3}]?
In reality my vector has hundreds of vectors that need to have keys added and be converted to hash-maps.
What leetwinski said, or:
(def input [["foo" "bar" 2]["biz" "baf" 3]])
(mapv (fn [[a b num]]
{:a a
:b b
:num num}) input)
If you need to convert a lot of data, maybe mapv is not the best option because it will keep the whole vector in memory at once. Normal map which creates a lazy seq, or a transducer might be better in that case.
A general solution:
(defn vectors->maps
"takes a vector of vectors containing values and a vector of keys
and returns a vector of maps such that each value at index n in the value
vector is paired with each key at index n in the keys vector
ex: (vectors->maps [["foo" "bar" 2] ["biz" "baf" 3]], [:a :b :num])
=> [{:a "foo", :b "bar", :num 2} {:a "biz", :b "baf", :num 3}]"
[vectors keys]
(mapv (partial zipmap keys) vectors))
Exercise for the reader: writing a spec for this function and generating tests for it to ferret out any edge cases
I define a function, which takes two parameters - map and a key. The key is referenced from the map parameter decomposition
(defn myfunc [{v k} k]
v)
when I call:
(myfunc {:a 10} :a)
It surprisingly produces expected result: 10
Similar thing in the let:
(let [{v k} {:a 10} k :a] v)
fails, because k is not defined at the moment, when first part is evaluated.
My question is: why decomposition inside function parameters behaves differently compared to decomposition in let expressions?
Macroexpanding the defn form I get the equivalent of this (removed .withMeta stuff and reformatted):
(def myfunc
(fn* myfunc
([p__11393 k]
(let* [map__11394 p__11393
map__11394 (if (seq? map__11394)
(apply hash-map map__11394)
map__11394)
v (get map__11394 k)]
v))))
So here we can see that the {v k} map is in fact first assigned to a local variable p__11393. Then the if statement tests if that variable is in fact a sequence and turns it into a hash-map if so, otherwise leaves it as it is. Importantly, the value assigned to k is looked up in the map after all of this happens, thus this works without error (and also would if :a wasn't in the map, get returns nil in that case).
On the other hand macroexpanding the let form I get
(let*
[map__11431
{:a 10}
map__11431
(if (seq? map__11431) (apply hash-map map__11431) map__11431)
v
(get map__11431 k)
k
:a]
v)
and here we can see that v gets the result of (get map__11431 k), but k isn't defined at this point yet, hence the error.
This isn't a complete answer, but the following does work:
user=> (defn myfunc [{v k} k] v)
#'user/myfunc
user=> (myfunc {:a 10} :a)
10
user=> (let [k :a {v k} {:a 10}] v)
10
user=>