How can I create an empty hash-map in clojure - clojure

It gives me a ArrayMap as I code
(class (hash-map))
But it comes out a HashMap when I code:
(class (hash-map "" ""))
Question is "How can I create an empty hash-map"?

Another possibility is to use pre-defined EMPTY field:
user=> (clojure.lang.PersistentHashMap/EMPTY)
{}
In my opinion it is better shows your intent.

You can create empty hash-map like this:
(. clojure.lang.PersistentHashMap create {})
(clojure.lang.PersistentHashMap/create {})
(clojure.lang.PersistentHashMap/EMPTY)
You can check the source code of hash-map:
user=> (source hash-map)
(defn hash-map
"keyval => key val
Returns a new hash map with supplied mappings. If any keys are
equal, they are handled as if by repeated uses of assoc."
{:added "1.0"
:static true}
([] {})
([& keyvals]
(. clojure.lang.PersistentHashMap (create keyvals))))
As you can see in the code, if you don't provide arguments, hash-map function returns {}, which is the instance of PersistentArrayMap.
If you really need the instance of empty PersistentHashMap, you can create it with the following code:
(. clojure.lang.PersistentHashMap create {})
You can check the class of created instance:
user=> (class (. clojure.lang.PersistentHashMap create {}))
clojure.lang.PersistentHashMap
user=> (class (clojure.lang.PersistentHashMap/create {}))
clojure.lang.PersistentHashMap
user=> (class (clojure.lang.PersistentHashMap/EMPTY)) ;; om-nom-nom's : much simpler
clojure.lang.PersistentHashMap
But, I'm not sure that doing this is good or necessary. Perhaps you code shouldn't depend on specific implementation class.

You shouldn't really need to worry about this. The runtime makes a judgement on the best implementation to use. PersistentArrayMap is preferred (ie. it's more efficient in time and space) for a small number of key/value pairs, but promotion to PersistentHashMap happens once the kv limit of 8 is crossed, see the relevant code for details
*clojure-version*
{:major 1, :minor 5, :incremental 1, :qualifier nil}
; map declared with {} with 8 kv pairs is ArrayMap
(type {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8})
=> clojure.lang.PersistentArrayMap
; map declared with {} with 9 kv pairs is HashMap
(type {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8 :i 9})
=> clojure.lang.PersistentHashMap
; assoc'ing 1 kv pairs into an ArrayMap is an ArrayMap (oddly)
(type (-> {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8}
(assoc :i 9)))
clojure.lang.PersistentArrayMap
; assoc'ing 2 kv pairs into an ArrayMap is an HashMap
(type (-> {:a 1 :b 2 :c 3 :d 4 :e 5 :f 6 :g 7 :h 8}
(assoc :i 9)
(assoc :j 10)))
clojure.lang.PersistentHashMap

Related

Getting the "unread" part of a string being read with Clojure's read-string

Clojure's (read-string) is really useful.
eg.
(read-string "{:a 1 :b 2} {:c 3 :d 4} [1 2 3]")
will give me the first object, the {:a 1 :b 2}
But how can I get the rest of string ie. "{:c 3 :d 4} [1 2 3]"
What's the reader equivalent of rest or drop ?
You can wrap the string in a StringReader, then wrap that in a PushbackReader, then read from that reader multiple times.
NB. the example below uses clojure.edn/read, as that is an edn-only reader meant for dealing with pure data; clojure.core/read is primarily meant for reading code and should never be used with untrusted inputs.
(require '[clojure.edn :as edn])
(def s "{:a 1 :b 2} {:c 3 :d 4} [1 2 3]")
;; Normally one would want to use with-open to close the reader,
;; but here we don't really care and we don't want to accidentally
;; close it before consuming the result:
(let [rdr (java.io.PushbackReader. (java.io.StringReader. s))
sentinel (Object.)] ; ← or just use ::eof as sentinel
(take-while #(not= sentinel %)
(repeatedly #(edn/read {:eof sentinel} rdr))))
;= ({:a 1, :b 2} {:c 3, :d 4} [1 2 3])
ClojureScript version of what should be the accepted answer by https://stackoverflow.com/users/232707/michał-marczyk
(require '[cljs.reader :as rdr])
(require '[cljs.tools.reader.reader-types :as reader-types])
(def s "{:a 1 :b 2} {:c 3 :d 4} [1 2 3]")
(let [pbr (reader-types/string-push-back-reader s)
sentinel ::eof]
(take-while #(not= sentinel %)
(repeatedly #(rdr/read {:eof sentinel} pbr))))
Probably not very idiomatic but straightforward
(->> (str "(" "{:a 1 :b 2} {:c 3 :d 4} [1 2 3]" ")")
(read-string))
then access to individual elements (you can also use brackets)
If you have a list within the string, you can preserve it via options given to read-string-
(def str-list "({:a 1 :b 2} {:c 3 :d 4} [1 2 3])")
(read-string {:read-cond :preserve} str-list)
;;=> ({:a 1 :b 2} {:c 3 :d 4} [1 2 3])
The source for the available options can be found in the doc string the of read function, i.e. (source read)from the REPL.

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]}

Macro to pass patterns and results to core.match/match as a vector

I am struggling on how to construct a macro that lets me pass patterns and results to core.match/match in the form of a vector. I would like to be able to do this:
(let [x {:a 1}
patterns [[{:a 2}] :high
[{:a 1}] :low]]
(my-match x patterns))
> :low
I have tried the below and several other approaches which do not work, unless I pass patterns as a literal.
(defmacro my-match [e ems]
`(m/match [~e] ~#ems))
(let [x {:a 1}
patterns [[{:a 2}] :high
[{:a 1}] :low]]
(my-match x patterns))
=> CompilerException java.lang.IllegalArgumentException: Don't know how to create ISeq from: clojure.lang.Symbol, compiling:(*cider-repl kontrakt*:106:10)
(let [x {:a 1}]
(my-match x [[{:a 2}] :high
[{:a 1}] :low]))
=> :low
Macros are expanded at compile time, so you cannot rely on runtime information (the value of a parameter) during expansion. The root problem is that you can't apply a macro in the same way you can apply a function.
In clojure, how to apply a macro to a list?
So you have to either resort to using eval:
(defmacro functionize [macro]
`(fn [& args#] (eval (cons '~macro args#))))
(defmacro my-match [e ems]
`(apply (functionize m/match) [~e] ~ems))
Or approach the problem in a different way (do runtime pattern matching instead of compile time pattern matching).
The simplest way to solve your problem is with a plain old map:
(ns clj.core
(:use tupelo.core))
(def x {:a 1} )
(def patterns { {:a 2} :high
{:a 1} :low } )
(spyx (get patterns x))
;=> (get patterns x) => :low
Since you have no "wildcard values", you don't need core.match at all. If you would like to match on wild-card values, please see the function wild-match? in the Tupelo library. Samples:
(wild-match? {:a :* :b 2}
{:a 1 :b 2}) ;=> true
(wild-match? [1 :* 3]
[1 2 3]
[1 9 3] )) ;=> true
(wild-match? {:a :* :b 2}
{:a [1 2 3] :b 2}) ;=> true

clojure: given a list of maps, get the total sum value of a specific key value

Input: [{:a "ID1" :b 2} {:a "ID2" :b 4}]
I want to only add up all the keys :b and produce the following:
Result: 6
I thought about doing a filter? to pull all the numbers into vector and add it all up but this seems like doing work twice. I can't use merge-with + here since the :a has a string in it. Do I use a reduce here with a function that will pull the appropriate key?
(reduce (fn [x] (+ (x :b))) 0 list-of-maps)
It would be even nicer if I could retain the map structure with updated value ({:a "ID1" :b 6}) but since I don't really need the other keys, just the total sum is fine.
I want to only add up all the keys :b and produce the following:
Result: 6
I believe workable code is:
(def m1 {:a 1, :b 2})
(def m2 {:a 11, :b 12})
(def m3 {:a 21, :b 22})
(def ms [m1 m2 m3])
(->> ms
(map :b)
(reduce +))
I feel use of ->> here can help readability in your situation.
This says to take action on ms, which is defined to be a vector of maps, threading incremental results through the remaining forms.
The first thing is to transform each entry of maps using the keyword :b as a function on each, extracing the value corresponding to that key, resulting in the sequence:
(2 12 22)
You can then apply reduce exactly as you intuit across that seq to get the result:
user=> (def m1 {:a 1, :b 2})
#'user/m1
user=> (def m2 {:a 11, :b 12})
#'user/m2
user=> (def m3 {:a 21, :b 22})
#'user/m3
user=> (def ms [m1 m2 m3])
#'user/ms
user=> (->> ms
#_=> (map :b)
#_=> (reduce +))
36
I'm a tad confused by what you intend by this part of the question:
It would be even nicer if I could retain the map structure with updated value ({:a "ID1" :b 6})
Do you want to have each value for :b across all maps contain the sum of them all in a result, or something else?
(reduce + (map :b list-of-maps))
This its simple but it works!
user=> (+ (your-map :b) (your-map :b))
or
user=> (def x [{:a "ID1" :b 2} {:a "ID2" :b 4}])
#'user/x
user=> (+ ((first x) :b) ((second x) :b))
6
user=>
or user=> (+ ((nth x 0) :b) ((nth x 1) :b))
6

update-in for sets in Clojure?

i have a series of items in a set like this:
(def my-set
#{
{:id "ab" :a 1 :b 2}
{:id "abc" :a 1 :b 2}
{:id "abcd" :a 1 :b 2}
}
)
: and I wish to update one of the items something like this :
(update-in-set my-set :id "abc" {:id "abc" :a 6 :b 20})
. that would return :
#{
{:id "ab" :a 1 :b 2}
{:id "abc" :a 6 :b 20}
{:id "abcd" :a 1 :b 2}
}
: Is there any Clojure built in function or other easy way to do this?
Update
In the end I did this:
(defn update-in-set [my-set key value new-record]
(merge (clojure.set/select #(not= (get % key) value) my-set ) new-record)
)
I wonder if you shouldn't be using a map rather than a set here, with id as the key. Then what you want to do could be easily performed with assoc.
You are having problems as sets don't really have the idea of updating values - each item is unique and either present or not - so what you need to do is remove the old value and add a new one. This could be done a little easier with conj and disj I think:
(conj (disj #{'a 'b 'c} 'a) 'e)
Which would remove 'a and add 'e. This assumes you have some way of getting the complete item from the "key".