Clojure read the content of a lazy-seq ({ }) - clojure

I'm new in Clojure. I have this problems:
I receive this data from a function:
({:lat 40.4167754, :lng -3.7037902, :address Madrid, Spain})
When I ask for the class, I get:
> class x
> clojure.lang.LazySeq
I need access to :lat, :lng, :address, but I don't know how.

Try this:
(defn mystery-fn []
(list {:lat 40.4167754, :lng -3.7037902, :address "Madrid, Spain"} )
)
(println :println (mystery-fn))
(prn :prn (mystery-fn))
(def a (first (mystery-fn)))
(prn :a a)
(def b (:lat a))
(prn :b b)
with output:
:reloading (tst.clj.core)
:println ({:lat 40.4167754, :lng -3.7037902, :address Madrid, Spain})
:prn ({:lat 40.4167754, :lng -3.7037902, :address "Madrid, Spain"})
:a {:lat 40.4167754, :lng -3.7037902, :address "Madrid, Spain"}
:b 40.4167754
Notice the difference between println and prn. Using prn, you get strings displayed with double-quotes which can help a lot when there are embedded spaces.
Also, when you want to label a printed output, it is often easier to use a keyword as the label like (prn :xyz ...) instead of (println "xyz = " ...).

Related

How to find the match from list of maps with list of keys in clojure

I am new to clojure.
I have a map s1 and a vector s2
(def s1 {:params [{:param :begin_date
:name "begin date"
:dtk :date
:param_operand :single}
{:param :begin_num
:name "begin num"
:dtk :numeric
:param_operand :single}
{:param :begin_num2
:name "begin num2"
:dtk :numeric
:biparam_operand :single}]})
(def s2 [:begin_date :begin_num])
I am trying to find the match from s1 with s2. I.E iterate through each element from s2 and find the match from s1.
If the match exists check the type for :dtk, if the type is :date then we accept that map.
This is the code which I have tried, when I had to find if the match exists using just one element from the list s2 - :begin_date
(defn test->map
[s1 s2]
(let [test-vec (->> s1
:params
(filter (fn [typet] (= (:param typet) :begin_date)))
first
:dtk)]
(when (= test-vec :date)
"done")))
How to modify the above function in a way that it should be able to iterate over the entire list of elements and find a match.
TIA.
One way to do it using keep:
(defn match [k]
(->> s1
:params
(keep
(fn [{:keys [param] :as v}]
(when (= param k)
v)))
first))
or by using for with :when:
(defn lookup [k]
(first
(for [{:keys [param] :as v} (:params s1)
:when (= param k)]
v)))
(This is bit similar to your approach with for and filter. Only :when does the filtering now, and I return the actual value instead of the string "done".)
With result:
(map lookup s2)
;; => ({:param :begin_date, :name "begin date", :dtk :date, :param_operand :single}
;; {:param :begin_num, :name "begin num", :dtk :numeric, :param_operand :single})
I would personally transform the current data structure s1 to a Clojure map (for example via reduce) once, so you can access the params by key and use select-keys. Then you don't have to iterate over the whole of s2 for every lookup.
(defn transform [s1]
(reduce
(fn [acc {:keys [param] :as v}]
(assoc acc param v))
{}
(:params s1)))
(transform s1)
;; => {:begin_date
;; {:param :begin_date, :name "begin date", :dtk :date, :param_operand :single},
;; :begin_num
;; {:param :begin_num, :name "begin num", :dtk :numeric, :param_operand :single},
;; :begin_num2
;; {:param :begin_num2,
;; :name "begin num2",
;; :dtk :numeric,
;; :biparam_operand :single}}
(select-keys (transform s1) s2)
;; => {:begin_date
;; {:param :begin_date, :name "begin date", :dtk :date, :param_operand :single},
;; :begin_num
;; {:param :begin_num, :name "begin num", :dtk :numeric, :param_operand :single}}

Converting string to nested map in Clojure

I have a file containing some text like:
1|apple|sweet
2|coffee|bitter
3|gitpush|relief
I want to work with this input using a map. In Java or Python, I would have made a nested map like:
{1: {thing: apple, taste: sweet},
2: {thing: coffee, taste: bitter},
3: {thing: gitpush, taste: relief}}
Or even a list inside the map like:
{1: [apple, sweet],
2: [coffee, bitter],
3: [grape, sour]}
The end goal is to access the last two column's data efficiently using the first column as the key.
I want to do this in Clojure and I am new to it. So far, I have succeeded in creating a list of map using the following code:
(def cust_map (map (fn [[id name taste]]
(hash-map :id (Integer/parseInt id)
:name name
:taste taste ))
(map #(str/split % #"\|") (line-seq (clojure.java.io/reader path)))))
And I get this, but it's not what I want.
({1, apple, sweet},
{2, coffee, bitter},
{3, gitpush, relief})
It would be nice if you can show me how to do the most efficient of, or both nested map and list inside map in Clojure. Thanks!
When you build a map with hash-map, the arguments are alternative keys and values. For example:
(hash-map :a 0 :b 1)
=> {:b 1, :a 0}
From what I understand, you want to have a unique key, the integer, which maps to a compound object, a map:
(hash-map 0 {:thing "apple" :taste "sweet"})
Also, you do not want to call map, which would result in a sequence of maps. You want to have a single hash-map being built.
Try using reduce:
(reduce (fn [map [id name taste]]
(merge map
(hash-map (Integer/parseInt id)
{:name name :taste taste})))
{}
'(("1" "b" "c")
("2" "d" "e")))
--- edit
Here is the full test program:
(import '(java.io BufferedReader StringReader))
(def test-input (line-seq
(BufferedReader.
(StringReader.
"1|John Smith|123 Here Street|456-4567
2|Sue Jones|43 Rose Court Street|345-7867
3|Fan Yuhong|165 Happy Lane|345-4533"))))
(def a-map
(reduce
(fn [map [id name address phone]]
(merge map
(hash-map (Integer/parseInt id)
{:name name :address address :phone phone})))
{}
(map #(clojure.string/split % #"\|") test-input)))
a-map
=> {1 {:name "John Smith", :address "123 Here Street", :phone "456-4567"}, 2 {:name "Sue Jones", :address "43 Rose Court Street", :phone "345-7867"}, 3 {:name "Fan Yuhong", :address "165 Happy Lane", :phone "345-4533"}}
I agree with #coredump that this is not concise, yet a quick solution to your code is using a list (or any other collection) and a nested map:
(def cust_map (map (fn [[id name taste]]
(list (Integer/parseInt id)
(hash-map :name name
:taste taste)))
(map #(clojure.string/split % #"\|") (line-seq (clojure.java.io/reader path)))))
This may be a somewhat naive view on my part, as I'm not all that experienced with Clojure, but any time I want to make a map from a collection I immediately think of zipmap:
(require '[clojure.java.io :as io :refer [reader]])
(defn lines-from [fname]
(line-seq (io/reader fname)))
(defn nested-map [fname re keys]
"fname : full path and filename to the input file
re : regular expression used to split file lines into columns
keys : sequence of keys for the trailing columns in each line. The first column
of each line is assumed to be the line ID"
(let [lines (lines-from fname)
line-cols (map #(clojure.string/split % re) lines) ; (["1" "apple" "sweet"] ["2" "coffee" "bitter"] ["3" "gitpush" "relief"])
ids (map #(Integer/parseInt (first %)) line-cols) ; (1 2 3)
rest-cols (map rest line-cols) ; (("apple" "sweet") ("coffee" "bitter") ("gitpush" "relief"))
rest-maps (map #(zipmap keys %) rest-cols)] ; ({:thing "apple", :taste "sweet"} {:thing "coffee", :taste "bitter"} {:thing "gitpush", :taste "relief"})
(zipmap ids rest-maps)))
(nested-map "C:/Users/whatever/q50663848.txt" #"\|" [:thing :taste])
produces
{1 {:thing "apple", :taste "sweet"}, 2 {:thing "coffee", :taste "bitter"}, 3 {:thing "gitpush", :taste "relief"}}
I've shown the intermediate results of each step in the let block as a comment so you can see what's going on. I've also tossed in lines-from, which is just my thin wrapper around line-seq to keep myself from having to type in BufferedReader. and StringReader. all the time. :-)

Updating a nested map within a vector within a map

I have a map within a vector within a map.
{ :label "Apparel & Accessories",
:img_class "itm_3",
:children [
{:id "sub1",
:label "Clothing",
:markup [:div]
}
{:id "sub2",
:label "Shoes & Footwear",
:markup [:div]
}
]
} `
What I want to do is dissoc the key :markup from all entries. I've been struggling with this for 2 hours. Only far I got is dissoc the key :children.
But the requirement is to remove :markup key only.
So final output should be
{ :label "Apparel & Accessories",
:img_class "itm_3",
:children [
{:id "sub1",
:label "Clothing",
}
{:id "sub2",
:label "Shoes & Footwear",
}
]
} `
Any contribution is highly appreciated.
It is a good habit to make the alteration 'all in one go':
(update m :children (fn [v] (assert (vector? v)) (mapv #(dissoc % :markup) v)))
Here m is only being referred to once. It would matter for instance if you were doing a swap!.
If you don't have a vector, yet want to create one on the fly then this will work:
(update m :children (fn [xs]
(->> xs
vec
(mapv #(dissoc % :markup)))))
But on the other hand there is no real need to be using vectors. The original solution without the assert works fine when :children is set to:
'({:id "sub1",
:label "Clothing",
:markup [:div]}
{:id "sub2",
:label "Shoes & Footwear",
:markup [:div]})
I solved it in two steps, not one unfortunately.
(def m { :label "Apparel & Accessories", :img_class "itm_3", :children [ {:id "sub1", :label "Clothing", :markup [:div] } {:id "sub2", :label "Shoes & Footwear", :markup [:div] } ] })
(defn dissoc-markup [child]
(dissoc child :markup))
(update m :children #(mapv dissoc-markup %))
If you are sure that you want to remove :markup whenever you see it, here is a simple way to do it:
(def your-data {:your-data "...."})
(clojure.walk/postwalk (fn [m]
(if (map? m)
(dissoc m :markup)
m))
your-data)
If your initial map is m, a possible solution is this
(assoc m :children (mapv #(dissoc % :markup) (m :children)))
It takes the value of :children, removes the :markup keys and replaces the result to the initial map.

How to print map key/values with the values quoted?

normally when you print a map, the values are unquoted.
(print {:abc "0" :def "1"}) results in {:abc 0 :def 1}. I would like the output to look like {:abc "0" :def "1"}
I attempted to use the map function to get at every key-value pair and that did not work.
This was my attempt:
(defn print-map [m]
(print "{")
(map #((print (first %) "\"" (second %) "\",")) m)
(print "}\n")
)
nothing from the map gets printed
just use pr/prn instead of print/println, since they generate the string that could be read back by reader, meaning the strings would be quoted:
user=> (prn {:a "10" :b 20 :c "21"})
{:a "10", :b 20, :c "21"}
nil
(print (str {:a "82834"}))
;{:a "82834"}
=> nil

Filter for Lists in Clojure

I am having a bit of difficulty with Lists in Clojure
I have a quick question concerning the filter function
Let's say I have a List composed of Maps
My code is:
(def Person {:name Bob } )
(def Person2 {:name Eric } )
(def Person3 {:name Tim } )
(def mylist (list Person Person2 Person3))
How would i go about filtering my list so that , for example: I want the list minus Person2 (meaning minus any map that has :name Eric)
Thank you very much to everybody helping me out. This is my last question I promise
For this purpose, it's better to use the 'remove' function. It takes a sequence, and removes elements on which it's predicate returns 'true'. It's basically the opposite of filter. Here is an example of it, and filter's usage for the same purposes, that I worked up via the REPL.
user> (def m1 {:name "eric" :age 32})
#'user/m1
user> (def m2 {:name "Rayne" :age 15})
#'user/m2
user> (def m3 {:name "connie" :age 44})
#'user/m3
user> (def mylist (list m1 m2 m3))
#'user/mylist
user> (filter #(not= (:name %) "eric") mylist)
({:name "eric", :age 32})
user> (remove #(= (:name %) "eric") mylist)
({:name "Rayne", :age 15} {:name "connie", :age 44})
As you can see, remove is a little bit cleaner, because you don't have to use not=. Also, when working with maps, you don't have to use the 'get' function unless you want it to return something special if a key isn't in the map. If you know the key you're looking for will be in the map, there is no reason to use 'get'. Good luck!
Suppose you have something like this:
(def Person {:name "Bob" } )
(def Person2 {:name "Eric" } )
(def Person3 {:name "Tim" } )
(def mylist (list Person Person2 Person3))
This would work:
(filter #(not= "Eric" (get % :name)) mylist)
user=> (filter (fn [person] (not= (person :name) "Eric")) mylist)
({:name "Bob"} {:name "Tim"})
or using a more compact syntax:
user=> (filter #(not= (% :name) "Eric") mylist)
({:name "Bob"} {:name "Tim"})