I can build a map with a list of vectors:
user=> (into {} (list (vector "a" "b") (vector "c" "d")))
{"a" "b", "c" "d"}
But if I try to do it with a list of lists, it fails:
user=> (into {} (list (list "a" "b") (list "c" "d")))
Execution error (ClassCastException) at user/eval3 (REPL:1).
class java.lang.String cannot be cast to class java.util.Map$Entry (java.lang.String and java.util.Map$Entry are in module java.base of loader 'bootstrap')
Why?
When you do (into {} coll)
it is the equivalent of (reduce conj {} coll)
so each element of your coll
is used as the second argument to conj
with a hash map as the first argument.
conj
is built into clojure.lang.RT
and it calls (in this case) APersistentMap.cons()
to add the element which is here: https://github.com/clojure/clojure/blob/master/src/jvm/clojure/lang/APersistentMap.java#L24
As you can see from that, if the argument is a MapEntry
, it'll get added to the hash map as a key/value pair: (seq my-hash-map)
produces a sequence of MapEntry
items.
If the argument is a vector that has two elements, it'll get added as a key/value pair where the key is the first element of that vector and the value is the second element of that vector.
Otherwise, it tries to convert the argument to a sequence and then casts each element to MapEntry
and adds it as a key/value pair.
When you pass a list of vectors (two-element vectors), they are added as key/value pairs per the second case above. When you pass a list of lists, the third case is invoked and it tries to cast each element of your inner list to a MapEntry
-- which fails because those elements are String
's.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With