Is it possible to map
collections without turning them into lists? I know about mapv
for vectors, but what about sets or maps. Is there a generic map
function that treats each collection as a functor (i.e. something mappable) and preserves its type after mapping?
Here is what i have mind vs whats happening now:
(map inc [1 2 3]) ; => [2 3 4], instead I get: (2 3 4)
(map inc #{1 2 3}) ; => #{3 2 4}, instead I get: (3 2 4)
(map inc {:a 1 :b 2 :c 3} ; => {:a 2 :b 3 :c 4}, instead I get: ClassCastException
The return values from your functions aren't lists exactly, that's simply how they are represented in the REPL when printed. They are really instances of clojure.lang.LazySeq
, as you can see here:
(class (map inc [1 2 3]))
=> clojure.lang.LazySeq
Rather than being mappable
, Clojure attempts to deal with all collections as being Sequable
. The Seq
abstraction allows many transformations to be composed together into pipelines of operations on a collection, where maintaining the original input type often doesn't make sense, especially in terms of performance (speed). In this case, the dynamic nature of Clojure is actually quite useful.
It's trivial to convert the output of operations to the collection type that you want:
(-> (map inc [1 2 3])
(vec))
or more simply, using into
:
(into [] (map inc [1 2 3]))
into
can be used for lists, maps and sets:
(into #{} (map inc #{1 2 3}))
=> #{4 3 2}
In your example where you attempt to map over a Hashmap, map
is passed a sequence of key-value pairs in the form of a clojure.lang.MapEntry
, so inc
which expects a number, can't work on it directly.
You could do something like this, using destructuring to pull out the keys and values:
(into {} (for [[k v] {:a 1 :b 2 :c 3}]
[k (inc v)]))
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