Convert vector of strings into hash-map in Clojure - data-structures

I have the following data structure:
["a 1" "b 2" "c 3"]
How can I transform that into a hash-map?
I want the following data structure:
{:a 1 :b 2 :c 3}

Use clojure.string/split and then use keyword and Integer/parseInt:
(->> ["a 1" "b 2" "c 3"]
(map #(clojure.string/split % #" "))
(map (fn [[k v]] [(keyword k) (Integer/parseInt v)]))
(into {}))
=> {:a 1, :b 2, :c 3}

and one more :)
(->> ["a 1" "b 2" "c 3"]
(clojure.pprint/cl-format nil "{~{:~a ~}}")
clojure.edn/read-string)
;;=> {:a 1, :b 2, :c 3}

(into {}
(map #(clojure.edn/read-string (str "[:" % "]")))
["a 1" "b 2" "c 3"])
;; => {:a 1, :b 2, :c 3}

(def x ["a 1" "b 2" "c 3"])
(clojure.edn/read-string (str "{:" (clojure.string/join " :" x) "}"))
;;=> {:a 1, :b 2, :c 3}

Related

Clojure For loop index numbers

How can I have the index of this for loop in this block of code:
(def numbers [:one :two :three :four :five])
(def colors [:green :red :blue :pink :yellow])
(def letters [:A :B :C :D :E])
(for [x numbers
i colors
j letters]
(println (str x " - " i " - " j)))
This code will print 125 lines and I want to have the index number with each line.
You need map-indexed:
(def numbers [:one :two :three :four :five])
(def colors [:green :red :blue :pink :yellow])
(def letters [:A :B :C :D :E])
(->> (for [x numbers
i colors
j letters]
(str x " - " i " - " j))
(map-indexed (fn [index value]
(str "Index: " index " : " value)))
(run! println))
Shorter version of (fn [index value] ...):
(map-indexed #(str "Index: " %1" : " %2))
Also consider calling one println with one long string instead of calling 125x println, it seems to be faster:
(->> (for [x numbers
i colors
j letters]
(str x " - " i " - " j))
(map-indexed #(str "Index: " %1 " : " %2 "\n"))
str/join
println)
(str/join is clojure.string/join)
Using atoms are discouraged in Clojure, but I think this is the simplest way:
(let [index (atom 0)]
(for [x numbers
i colors
j letters]
(do (swap! index inc)
(println (str #index ": " x " - " i " - " j)))))
There are many options for this. Here are a few:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[clojure.string :as str]
[tupelo.core :as t]
))
(def numbers [:one :two :three :four :five])
(def colors [:green :red :blue :pink :yellow])
(def letters [:A :B :C :D :E])
(verify
; The tupelo.core/indexed function will add a 0-based index to each element in a sequence
(is= (t/indexed [:a :b :c])
[[0 :a]
[1 :b]
[2 :c]]))
The tupelo.core/indexed function will add a 0-based index to each element in a sequence, which is often handy.
(verify
; tupelo.core/map-let allows you to assign a local name to each item from multiple sequences
; but behaves like `map`, not `for`
(let [vecs (t/map-let [n numbers
c colors
l letters]
[n c l]) ; return a vector containing each item in sequence
]
(is= vecs
[[:one :green :A]
[:two :red :B]
[:three :blue :C]
[:four :pink :D]
[:five :yellow :E]])))
Using t/map-let allows you to give a local name to the elements of each sequence, but does not create the cartesian product like with for.
(verify
(t/let-spy-pretty ; or just `let` to suppress the "spy" printing
[
; using (mapv vector ...) will also place each triple into a vector
vecs (mapv vector numbers colors letters)
strs (mapv #(str/join " - " %) vecs)
strs-idx (t/indexed strs)
lines (t/forv [[the-idx the-str] strs-idx]
(str the-idx ": " the-str))]
(is= vecs
[[:one :green :A]
[:two :red :B]
[:three :blue :C]
[:four :pink :D]
[:five :yellow :E]])
(is= strs
[":one - :green - :A"
":two - :red - :B"
":three - :blue - :C"
":four - :pink - :D"
":five - :yellow - :E"])
(is= strs-idx
[[0 ":one - :green - :A"]
[1 ":two - :red - :B"]
[2 ":three - :blue - :C"]
[3 ":four - :pink - :D"]
[4 ":five - :yellow - :E"]])
(is= lines
["0: :one - :green - :A"
"1: :two - :red - :B"
"2: :three - :blue - :C"
"3: :four - :pink - :D"
"4: :five - :yellow - :E"])))
One option is to use range. (range 125) is all the integers 0 to 124 inclusive.
(def numbers [:one :two :three :four :five])
(def colors [:green :red :blue :pink :yellow])
(def letters [:A :B :C :D :E])
(for [x numbers
i colors
j letters
n (range (* x i j))]
(println (str x " - " i " - " j " line " n)))

Convert hashmap to hash-string-map in Clojure [duplicate]

This question already has answers here:
How to convert a clojure keyword into a string?
(5 answers)
Closed 2 months ago.
I have the following data structure:
{:a {:x 1 :y 2} :b {:w 1 :z 2} :c {:m 1 :n 2}}
How can I transform that into a string-hash-map?
I want the following data structure:
{"a" {:x 1 :y 2} 1 {:w 1 :z 2} 2 "c" {:m 1 :n 2}}
(let [data {:a {:x 1 :y 2} :b {:w 1 :z 2} :c {:m 1 :n 2}}]
(update-keys data name))
=> {"a" {:x 1, :y 2}, "b" {:w 1, :z 2}, "c" {:m 1, :n 2}}

Clojure thread-first with filter function

I'm having a problem stringing some forms together to do some ETL on a result set from a korma function.
I get back from korma sql:
({:id 1 :some_field "asd" :children [{:a 1 :b 2 :c 3} {:a 1 :b 3 :c 4} {:a 2 :b 2 :c 3}] :another_field "qwe"})
I'm looking to filter this result set by getting the "children" where the :a keyword is 1.
My attempt:
;mock of korma result
(def data '({:id 1 :some_field "asd" :children [{:a 1 :b 2 :c 3} {:a 1 :b 3 :c 4} {:a 2 :b 2 :c 3}] :another_field "qwe"}))
(-> data
first
:children
(filter #(= (% :a) 1)))
What I'm expecting here is a vector of hashmaps that :a is set to 1, i.e :
[{:a 1 :b 2 :c 3} {:a 1 :b 3 :c 4}]
However, I'm getting the following error:
IllegalArgumentException Don't know how to create ISeq from: xxx.core$eval3145$fn__3146 clojure.lang.RT.seqFrom (RT.java:505)
From the error I gather it's trying to create a sequence from a function...though just not able to connect the dots as to why.
Further, if I separate the filter function entirely by doing the following:
(let [children (-> data first :children)]
(filter #(= (% :a) 1) children))
it works. I'm not sure why the first-thread is not applying the filter function, passing in the :children vector as the coll argument.
Any and all help much appreciated.
Thanks
You want the thread-last macro:
(->> data first :children (filter #(= (% :a) 1)))
yields
({:a 1, :b 2, :c 3} {:a 1, :b 3, :c 4})
The thread-first macro in your original code is equivalent to writing:
(filter (:children (first data)) #(= (% :a) 1))
Which results in an error, because your anonymous function is not a sequence.
The thread-first (->) and thread-last (->>) macros are always problematical in that it is easy to make a mistake in choosing one over the other (or in mixing them up as you have done here). Break down the steps like so:
(ns tstclj.core
(:use cooljure.core) ; see https://github.com/cloojure/tupelo/
(:gen-class))
(def data [ {:id 1 :some_field "asd"
:children [ {:a 1 :b 2 :c 3}
{:a 1 :b 3 :c 4}
{:a 2 :b 2 :c 3} ]
:another_field "qwe"} ] )
(def v1 (first data))
(def v2 (:children v1))
(def v3 (filter #(= (% :a) 1) v2))
(spyx v1) ; from tupelo.core/spyx
(spyx v2)
(spyx v3)
You will get results like:
v1 => {:children [{:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1} {:c 3, :b 2, :a 2}], :another_field "qwe", :id 1, :some_field "asd"}
v2 => [{:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1} {:c 3, :b 2, :a 2}]
v3 => ({:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1})
which is what you desired. The problem is that you really needed to use thread-last for the filter form. The most reliable way of avoiding this problem is to always be explicit and use the Clojure as-> threading form, or, even better, it-> from the Tupelo library:
(def result (it-> data
(first it)
(:children it)
(filter #(= (% :a) 1) it)))
By using thread-first, you accidentally wrote the equivalent of this:
(def result (it-> data
(first it)
(:children it)
(filter it #(= (% :a) 1))))
and the error reflects the fact that the function #(= (% :a) 1) can't be cast into a seq. Sometimes, it pays to use a let form and give names to the intermediate results:
(let [result-map (first data)
children-vec (:children result-map)
a1-maps (filter #(= (% :a) 1) children-vec) ]
(spyx a1-maps))
;;-> a1-maps => ({:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1})
We could also look at either of the two previous solutions and notice that the output of each stage is used as the last argument to the next function in the pipeline. Thus, we could also solve it with thread-last:
(def result3 (->> data
first
:children
(filter #(= (% :a) 1))))
(spyx result3)
;;-> result3 => ({:c 3, :b 2, :a 1} {:c 4, :b 3, :a 1})
Unless your processing chain is very simple, I find it is just about always clearer to use the it-> form to be explicit about how the intermediate value should be used by each stage of the pipeline.
I'm not sure why the first-thread is not applying the filter function, passing in the :children vector as the coll argument.
This is precisely what the thread-first macro does.
From the clojuredocs.org:
Threads the expr through the forms. Inserts x as the
second item in the first form, making a list of it if it is not a
list already.
So, in your case the application of filter ends up being:
(filter [...] #(= (% :a) 1))
If you must use thread-first (instead of thread-last), then you can get around this by partially applying filter and its predicate:
(->
data
first
:children
((partial filter #(= (:a %) 1)))
vec)
; [{:a 1, :b 2, :c 3} {:a 1, :b 3, :c 4}]

Clojure sort map over value

I'm trying to sort a map over the values.
The input-map looks like:
{:Blabla 1, :foo 1, :bla-bla 1, :Bla 2, :bla/bla 1, :bla 4, :blub 2, :hello 1, :Foo 2}
The output should look like:
{:bla 4 :Bla 2 :blub 2 :Foo 2 :Blabla 1 :bla-bla 1 :bla/bla 1 :foo 1 :hello 1}
I used sorted-map-by like in the documentation here:
http://clojuredocs.org/clojure.core/sorted-map-by
(defn sort-keyword-list [texts]
(let [results (word-counts texts)]
;results is now {:Blabla 1, :foo 1, :bla-bla 1, :Bla 2, :bla/bla 1, :bla 4, :blub 2, :hello 1, :Foo 2}
(into (sorted-map-by (fn [key1 key2]
(compare [(get results key2) key2]
[(get results key1) key1])))
results))
)
Well I found out that this solution works only if the keywords have no special characters like "/" or "-" inside. Is this a known bug?
So how can I sort a map by values quickly without writing a own and slowly sort-algorithm?
In my Clojure 1.6.0 REPL, the code in the question already sorts by value:
user=> (into (sorted-map-by (fn [key1 key2]
(compare [(get x key2) key2]
[(get x key1) key1])))
x)
{:bla 4, :blub 2, :Foo 2, :Bla 2, :bla/bla 1, :hello 1, :foo 1, :bla-bla 1, :Blabla 1}
If you want entries with the same value to be sorted by key, you need to stringify the keys. Here's why:
user=> x
{:bla-bla 1, :Blabla 1, :bla/bla 1, :hello 1, :bla 4, :foo 1, :Bla 2, :Foo 2, :blub 2}
user=> (sort (keys x))
(:Bla :Blabla :Foo :bla :bla-bla :blub :foo :hello :bla/bla)
user=> (sort (map str (keys x)))
(":Bla" ":Blabla" ":Foo" ":bla" ":bla-bla" ":bla/bla" ":blub" ":foo" ":hello")
Here is a solution based on the suggestion by #user100464 with explicit considerations of comparison of keys, when the values are the same.
Note: I choose to sort decreasingly by reversing the order of the arguments to the comparisons: (compare (x k2) (x k1)) and (compare k2 k1).
(defn sort-by-value-then-key [x]
(into (sorted-map-by (fn [k1, k2]
(let [v_c (compare (x k2) (x k1))]
(if (= 0 v_c)
(compare k2 k1)))))
x))
One may customize at (compare k2 k1) to implement more elaborate key comparison.

Clojure: I have many sorted maps and want to reduce in order all there values a super maps of keys -> vector

I have seen this but can't work out how to apply it (no pun intended) to my situation.
I have a sorted list of maps like this: (note there can be more than two keys in the map)
({name1 3, name2 7}, {name1 35, name2 7}, {name1 0, name2 3})
What I am after is this data structure afterwards:
({:name1 [3,35,0]}, {:name2 [7,7,3]})
Ive been struggling with this for a while and cant seem to get anywhere near.
Caveats: The data must stay sorted and I have N keywords not just two.
I'd go for merge-with with some preprocessing added:
(def maps [{:a :b :e :f} {:a :c} {:a :d :e :g}])
(apply merge-with concat (for [m maps [k v] m] {k [v]}))
>>> {:e (:f :g), :a (:b :c :d)}
I think the function you want is merge-with:
user=> (def x {:a 1 :b 2})
user=> (def y {:a 3 :b 4})
user=> (merge-with vector x y)
{:a [1 3], :b [2 4]}
user=>
user=> (def z {:a 5 :b 6 :c 7})
user=> (merge-with vector x y z)
{:a [[1 3] 5], :c 7, :b [[2 4] 6]} ; oops
user=> (merge-with #(vec (flatten (vector %1 %2))) x y z)
{:a [1 3 5] :b [2 4 6] :c 7}
user=>
This is my attempt at the problem. It is not as elegant as Rafal's solution.
(def maps [{:a :b :e :f} {:a :c} {:a :d :e :g}])
(apply merge-with #(if (list? %1) (conj %1 %2) (list %1 %2)) maps)
>>> {:a (:d :b :c), :e (:f :g)}

Resources