Remove commas from hashmap - clojure

I'm using a function that returns a map
{:a "A", :b "B"}
As you can see it has commas as separators. Is there a simple way to transform this into usual hashmap without commas?:
{:a "A" :b "B"}

as noisessmith pointed out, there is no (programmatic) difference between those two since , is whitespace in clojure source code:
user=> (= {:a "A", :b "B"} {:a "A" :b "B"})
true
user=> (map type [{:a "A", :b "B"} {:a "A" :b "B"}])
(clojure.lang.PersistentArrayMap clojure.lang.PersistentArrayMap)
PS: It's up to the underlying pretty-printer (the default clojure one prints ,) to add , to aid readabiliy

Related

Clojure custom function for threading macro

I have a map and I want to write a custom function for updating it.
(-> {:a 1 :b 2}
(fn [x] (update x :a inc)))
This of course is a simple example and could be easily done without the function wrapped around the update, but it shows what I want to do. But this gives me the following error.
Syntax error macroexpanding clojure.core/fn at (core.clj:108:1).
{:a 1, :b 2} - failed: vector? at: [:fn-tail :arity-1 :params] spec: :clojure.core.specs.alpha/param-list
{:a 1, :b 2} - failed: (or (nil? %) (sequential? %)) at: [:fn-tail :arity-n] spec: :clojure.core.specs.alpha/params+body
I don't get why this is not working, since the threading macro should but my map as first parameter in the function, right?
You can always use macroexpand to see what happened. In your case, macroexpand will return you:
(fn {:a 1, :b 2} [x] (update x :a inc))
obviously this is not a valid function. But if you tweak it this way:
(-> {:a 1 :b 2}
(#(update % :a inc)))
the expanded form will then become valid:
(#(update % :a inc) {:a 1, :b 2})
You don't put a function itself to be called, but call the function without the first parameter, For your example it would be:
> (-> {:a 1 :b 2}
(update :a inc))
{:a 2, :b 2}
This is easier to see by expanding the macro in each case
> (macroexpand-1 '(-> {:a 1 :b 2} (update :a inc)))
(update {:a 1, :b 2} :a inc)
> (macroexpand-1 '(-> {:a 1 :b 2} (fn [x] (update x :a inc))))
(fn {:a 1, :b 2} [x] (update x :a inc))
As #jas and #rmcv pointed out, I was giving the threading macro the function itself, not the call of a function without the argument. So in short terms the solution would be
(-> {:a 1 :b 2}
((fn [x] (update x :a inc))))
I don't think any of these solutions are the simplest. I would propose choosing one of the following:
A. Use the normal threading form:
(-> {:a 1, :b 2}
(update :a inc)) => {:a 2, :b 2}
Everyone is used to seeing this and can understand it easily. Since you have already rejected this approach, I assume you think the code is clearer by using a named parameter.
B. Use a named function
(defn updater [x] (update x :a inc))
(-> {:a 1, :b 2}
updater) => {:a 2, :b 2}
(-> {:a 1, :b 2}
(updater)) => {:a 2, :b 2}
This is more how the -> form was envisioned to work. I think the 2nd version is the clearest, as it is the most consistent where all function expressions have parentheses (single arg or multi-arg).
C. Consider using the it-> macro from the Tupelo Library:
(it-> {:a 1, :b 2}
(update it :a inc)) => {:a 2, :b 2}
Much like the named function, the expression is normal Clojure form without the "invisible" parameter silently inserted into the update expression. The pronoun it serves as the temporary placeholder for the threaded value (an idea copied from Groovy). Simple, explicit, and flexible, since the it can be in the first, last, or any other parameter location:
(it-> 1
(inc it) ; thread-first or thread-last
(+ it 3) ; thread-first
(/ 10 it) ; thread-last
(str "We need to order " it " items." ) ; middle of 3 arguments
;=> "We need to order 2 items." )

How to merge maps and get a map of lists?

Let's say we a list of maps. Maps all have the same keywords, but we don't know the keywords beforehand.
[{:a 1 :b 2} {:a 3 :b 4}]
And what would be the idiomatic way of merging this list into such a map:
{:a [1 3]
:b [2 4]}
Doesn't seem hard, however as I start to implement the function, it gets super ugly and repetitive. I have a feeling that there are much cleaner ways of achieving this.
Thank you
You can actually get a pretty elegant solution by using several functions from the standard library:
(defn consolidate [& ms]
(apply merge-with conj (zipmap (mapcat keys ms) (repeat [])) ms))
Example:
(consolidate {:a 1 :b 2} {:a 3 :b 4})
;=> {:a [1 3], :b [2 4]}
One cool thing about this solution is that it works even if the maps have different key sets.
i would rather use double reduction to "merge" them with update:
(defn merge-maps-with-vec [maps]
(reduce (partial reduce-kv #(update %1 %2 (fnil conj []) %3))
{} maps))
user> (merge-maps-with-vec [{:a 1 :b 2} {:a 3 :b 4 :c 10}])
{:a [1 3], :b [2 4], :c [10]}
It is not as expressive as #Sam Estep's answer, but on the other hand it doesn't generate any intermediate sequences (like every-key-to-empty-vector map which also needs one extra pass through every entry of every map). Of course, premature optimizations are bad in general, but it won't hurt here i guess. Though the reduce based solution looks a bit more obscure, but being put into a library with proper docs it would not look as obscure to the end user (or to yourself a year after)
While many solutions are possible, here is one that uses some of the convenience functions in the Tupelo library:
(ns clj.core
(:use tupelo.core)
(:require [tupelo.schema :as ts]
[schema.core :as s] ))
(s/defn gather-keys
[list-of-maps :- [ts/KeyMap]]
(newline)
(let [keys-vec (keys (first list-of-maps))]
(s/validate [s/Keyword] keys-vec) ; verify it is a vector of keywords
(apply glue
(for [curr-key keys-vec]
{curr-key (forv [curr-map list-of-maps]
(get curr-map curr-key))} ))))
(deftest t-maps
(spyx
(gather-keys [{:a 1 :b 2}
{:a 3 :b 4} ] )))
(gather-keys [{:a 1, :b 2} {:a 3, :b 4}]) ;=> {:a [1 3], :b [2 4]}
Note that this solution assumes that each input map has an identical set of keys. Normally I'd want to enforce that assumption with a sanity check in the code as well.
Looking at the answer from Sam, I would rewrite it with some temporary variables to help document the sub-steps:
(defn consolidate-keys [list-of-maps]
(let [keys-set (set (mapcat keys list-of-maps))
base-result (zipmap keys-set (repeat [] )) ]
(apply merge-with conj base-result list-of-maps)))
(consolidate-keys [ {:a 1 :b 2}
{:a 3 :z 9} ] )
;=> {:z [9], :b [2], :a [1 3]}

Strange thing with apply/merge-with, in Clojure

Who can explain me this fact:
user> ((partial merge-with +) {:a 1} {:a 2})
{:a 3}
user> (apply (partial merge-with +) ({:a 1} {:a 2}))
nil
Why do I get nil in the second case?
What is wrong with the second line?
The expression
({:a 1} {:a 2})
evaluates to nil. Maps in Clojure are functions which takes a key and returns the corresponding value. The expression
(let [f {:a 1}]
(f {:a 2}))
which is equivalent to ({:a 1} {:a 2}) tries to lookup the key {:a 2} in the map {:a 1} and since there is no such key in the map nil is returned.
Going back to your original problem, all you have to do is to change the list ({:a 1} {:a 2}) to a vector [{:a 1} {:a 2}] and it will work as expected. Note also that you don't need partial in this particular case, (apply merge-with + [{:a 1} {:a 3}]) will work just fine.
In the second case, when you ({:a 1} {:a 2}), as maps act as functions which get values from them, what you're doing is equivalent to (get {:a 1} {:a 2}) and, as {:a 2} is not a key in {:a 1}, you get nil. Then, aplying the function over nil gets nil.
What you have to do is either quote the list, such as not evaluate it as a function application
user=> (apply (partial merge-with +) '({:a 1} {:a 2}))
{:a 3}
or use a vector (which is more idiomatic in clojure)
user=> (apply (partial merge-with +) [{:a 1} {:a 2}])
{:a 3}
Juan Manuel

Update-in nested map

I'm new to clojure and I've been staring at this for some time, I'm sure there's something basic I just don't see. I want to conj two sets, but they're nested, example:
(def foo {:b #{:test}})
(def bar {:a {:b #{:ab}} :c :d})
I tried:
=>(update-in bar [:a :b] conj (:b foo) )
{:a {:b #{#{:test} :ab}}, :c :d}
I guess that makes sense, but what I wanted was {:a {:b #{:test :ab}}, :c :d}
I just can't seem how to get either #{:test} out of the set to conj it, or to properly access :b as a set given the update-in syntax.
Any help is enormously appreciated.
You need to use into instead of conj:
(update-in bar [:a :b] into (:b foo))
;= {:a {:b #{:test :ab}}, :c :d}

How do I transform a sequences of maps into a sequence of maps with selected keys?

I have a sequence of map like this
({:a 1 :b 2 : c 4} {:a 3 :b 3 :d 4})
And I want to turn this into a sequence of more compact maps that just have the :a and :b keys, like this:
({:a 1 :b 2} {:a 3 :b 3})
What's the most concise way to do this?
The built-in function select-keys is what you're looking for.
(let [in [{:a 1 :b 2 :c 4} {:a 3 :b 3 :d 4}]]
(map #(select-keys % [:a :b])
in))
A more generic solution would be to write a function that takes the keys you want to keep and returns a fn on maps. Then map it over the sequence of maps:
(defn keep-keys
[ks]
(fn [m] (select-keys m ks)))
(map (keep-keys [:a :b]) '({:a 1 :b 2 :c 4} {:a 3 :b 3 :d 4}))