Reputation: 5154
As an exercise I am trying to re-implement zipmap
. The following statement works fine, it takes keys and values and turns them into a map:
user=> (into {} (mapv vector [:a :b] [1 2]))
{:a 1, :b 2}
However, I run into problem when I try to turn the above statement into a function:
user=> ((fn [& xs] (into {} (mapv vector xs))) [:a :b] [1 2])
IllegalArgumentException Vector arg to map conj must be a pair
Clojure.lang.ATransientMap.conj (ATransientMap.java:37)
What is the problem with my implementation and why it happens?
Upvotes: 0
Views: 260
Reputation: 231
The problem is the way you are getting your arguments to your anonymous function
[& xs]
will roll the arguments up into a list
so in your non-function version (into {} (mapv vector [:a :b] [1 2]))
you're mapping vector over two collections
in your function version you're mapping vector over one collection and basically doing this:
(into {} (mapv vector [[:a :b] [1 2]]))
which evaluates to (into {} [[[:a :b] [1 2]]]])
which gives you an error
Possible Solutions:
Since you're trying to re-implement zipmap, why not use the same arg list it uses [keys vals]
instead of [& xs]
and write your function like this:
((fn [keyz valz] (into {} (mapv vector keyz valz))) [:a :b] [1 2])
You could also unroll the collection yourself:
((fn [& xs] (into {} (mapv vector (first xs) (second xs)))) [:a :b] [1 2])
I think using the explicit arg list is a more precise way of going about it since you're expecting to get two specific collections
Upvotes: 3
Reputation: 16194
You can make your sample work by using apply
to apply xs
to mapv vector
.
((fn [& xs] (into {} (apply mapv vector xs))) [:a :b] [1 2])
This is due to how variadic arguments are bound to xs
. Without apply
, you'd essentially be calling it like this (mapv vector [[:a :b] [1 2]])
but you want it to be called like (mapv vector [:a :b] [1 2])
.
Upvotes: 4