I made assoc-in like this
(defn process-pubaccess-nb [conn books]
(map #(assoc-in % [:book/publication :publication/access] (get-rules-ids % conn) ) books)
)
I want to add condition don't made assoc-in if get-rules-ids returns nil. I tried to add when-let, but I had errors.
For example if I get
(def lib [{:book/name "one" :book/pid "1" :book/publication {:publication/pid "11"} }
{:book/name "two" :book/pid "2":book/publication {:publication/pid "22"} }
{:book/name "three" :book/pid "3" }])
I want to have
({:book/name "one", :book/pid "1", :book/publication {:publication/pid "11", :publication/access "test"}}
{:book/name "two", :book/pid "2", :book/publication {:publication/pid "22", :publication/access "test"}}
{:book/name "three", :book/pid "3"})
Now I have without condition
({:book/name "one", :book/pid "1", :book/publication {:publication/pid "11", :publication/access "test"}}
{:book/name "two", :book/pid "2", :book/publication {:publication/pid "22", :publication/access "test"}}
{:book/name "three", :book/pid "3", :book/publication {:publication/access nil}})
Maxx
Amar's solution works but it isn't clear the cost of calling get-rules-ids versus pre-filtering the collection.
Here is the filtered option for posterity:
(defn process-pubaccess-nb
[conn books]
(map #(assoc-in % [:book/publication :publication/access] (get-rules-ids % conn))
(filter :book/publication books)))
Also, if the collection may be considerable in size, transducers could be more performant.
One way to do this is with if-let.
(defn process-pubaccess-nb
[conn books]
(map #(if-let [access (get-rules-ids % conn)]
(assoc-in % [:book/publication :publication/access] access)
%)
books))
Related
I am new to Clojure and learning the properties of various data structures in Clojure. Here, I have a list of vectors as follows:
(["1" "Christiano Ronaldo" "Portugal" "35"]
["2" "Lionel Messi" "Argentina" "32"]
["3" "Zinedine Zidane" "France" "47"])
where the first element of each vector is the id. How do I filter out single vectors from the list based on the id? For eg., id = 1 should return
["1" "Christiano Ronaldo" "Portugal" "35"]
I tried doing the same on a nested-map:
(def footballers
[
{:id 1 :name "Christiano Ronaldo" :country "Portugal" :age 35}
{:id 2 :name "Lionel Messi" :country "Argentina" :age 32}
{:id 3 :name "Zinedine Zidane" :country "France" :age 47}
]
)
and was successful using the filter function
(filter #(= (:id %) 1) footballers)
Result:
({:id 1, :name "Christiano Ronaldo", :country "Portugal", :age 35})
How do I do the same in a list of vectors using filter function?
(filterv #(= "1" (first %)) footballers) ; or `filter`
;=> [["1" "Christiano Ronaldo" "Portugal" "35"]] ; vector containing 1 vector
Please see this list of documentation.
I have data in the following format:
[["i1" "i2"]
['("A" "B" "C" "D") '("red" "blue" "green" "yellow")]]
I want to transform this to get a new data structure:
[["column" "value"]
["i1" "A"]
["i1" "B"]
["i1" "C"]
["i1" "D"]
["i2" "red"]
["i2" "blue"]
["i2" "green"]
["i2" "yellow"]]
Any help with this difficult problem would be great.
My attempts to far have involved using nested "for" statements, but I cannot get the resulting vectors in the same level as the header vector, despite many attempts to convert the results. I have also used "interleave" and "repeat" on the value for column, but that too creates lists at the wrong level.
(defn doit [[is vss]]
(vec (cons
["column" "value"]
(mapcat (fn [i vs] (mapv (fn [v] [i v]) vs)) is vss))))
(defn convert
[[header data]]
(->> (mapcat #(map vector (repeat %) %2) header data)
(cons ["column" "value"])))
(convert '[["i1" "i2"] [("A" "B" "C" "D") ("red" "blue" "green" "yellow")]])
;; => (["column" "value"]
;; ["i1" "A"] ["i1" "B"] ["i1" "C"] ["i1" "D"]
;; ["i2" "red"] ["i2" "blue"] ["i2" "green"] ["i2" "yellow"])
(defn conform
[[ks & rows]]
(mapcat
(fn [row]
(mapcat (fn [k val]
(map (partial vector k) val))
ks row))
rows))
Your example:
(conform [["i1" "i2"] ['("A" "B" "C" "D") '("red" "blue" "green" "yellow")]])
=> (["i1" "A"] ["i1" "B"] ["i1" "C"] ["i1" "D"]
["i2" "red"] ["i2" "blue"] ["i2" "green"] ["i2" "yellow"])
Bonus:
(conform [["i1" "i2"]
['("A" "B" "C" "D") '("red" "blue" "green" "yellow")]
['("E" "F" "G" "H") '("Mara" "Lara" "Clara" "Foxy")]])
=> (["i1" "A"] ["i1" "B"] ["i1" "C"] ["i1" "D"]
["i2" "red"] ["i2" "blue"] ["i2" "green"] ["i2" "yellow"]
["i1" "E"] ["i1" "F"] ["i1" "G"] ["i1" "H"]
["i2" "Mara"] ["i2" "Lara"] ["i2" "Clara"] ["i2" "Foxy"])
More bonus:
(conform [["i1" "i2" "i3"]
['("A" "B" "C" "D") '("red" "blue" "green" "yellow") ["Ufo"]]
['("E" "F" "G" "H") '("Mara" "Lara" "Clara" "Foxy") ["Orange" "Apple"]]])
=> (["i1" "A"] ["i1" "B"] ["i1" "C"] ["i1" "D"]
["i2" "red"] ["i2" "blue"] ["i2" "green"] ["i2" "yellow"]
["i3" "Ufo"]
["i1" "E"] ["i1" "F"] ["i1" "G"] ["i1" "H"]
["i2" "Mara"] ["i2" "Lara"] ["i2" "Clara"] ["i2" "Foxy"] ["i3" "Orange"] ["i3" "Apple"])
Creating a map from the result is easy:
(reduce (fn [acc [k v]]
(update-in acc [k] (fnil conj []) v)) {} *1 )
=> {"i3" ["Ufo" "Orange" "Apple"],
"i2" ["red" "blue" "green" "yellow" "Mara" "Lara" "Clara" "Foxy"],
"i1" ["A" "B" "C" "D" "E" "F" "G" "H"]}
There are some fun answers here. I will just add that conceptually, I think this is a good place for for. For each item in the first vector, you want to pair it with items from the corresponding lists in the second vector.
(defn convert [[cols vals]]
(vec (cons ["column" "value"] ;; turn the list into a vector.
(for [i (range (count cols)) ;; i <- index over columns
j (nth vals i)] ;; j <- items from the ith list
[(nth cols i) j])))) ;; [col val]
user=>(convert data)
This is easily modified to handle more vectors of values:
(defn convert [[cols & vals]]
(cons ["column" "value"]
(mapcat #(for [i (range (count cols))
j (nth % i)]
[(nth cols i) j])
vals)))
Not idiomatic, but did the trick.
((fn [coll]
(let [ks (first coll) vs (last coll)]
(cons
'("column" "value")
(partition 2 (concat
(interleave (repeat (first ks)) (first vs))
(interleave (repeat (last ks)) (last vs)))))))
[["i1" "i2"]
['("A" "B" "C" "D") '("red" "blue" "green" "yellow")]])
How i can count mobile and web access discarding a nil values from a list of maps? the output should be anything like this " Statistic mobile = 1 web = 2", but all is imutable on other languagens a simple i++ resolve but how is in clojure. thanks.
def data [{:name "app1" :type "mobile" }
{:name "site1" :type "web" }
{:name "site1" :type "web" }
{:name "boot" :type nil }]
(frequencies (map :type data))
gives
{"mobile" 1, "web" 2, nil 1}
user=> (for [[k v] (group-by :type data) :when k] [k (count v)])
(["mobile" 1] ["web" 2])
What would be a best way to impose a condition on the nested fields of complex nested structure like...
{
:aa {:a "a_val",:b "b_val"},
:qq {:abc
{
:x1 {:x "abc",:u "ee"},
:x2 {:y "abc",:i "ee"},
:x3 {:x "abc",:i "ee"}
}
},
:ww {:xyz {
:y1 {:x "abc",:u "ee"},
:y2 {:y "abc",:i "0"},
:y3 {:x "abc",:i "ee"}
}
}
}
I want to check whether the "i" part exist and has value "0" in each of aa,qq and ww and depending upon that exclude(or perform any operation) on aa,qq and ww. For example if "ww" has "i"="0" at that position then get a map like below
{
:ww {:xyz {
:y1 {:x "abc",:u "ee"},
:y2 {:y "abc",:i "0"},
:y3 {:x "abc",:i "ee"}
}
}
}
user> (defn vvals [m] (when (map? m) (vals m)))
'user/vvals
user> (filter #(some #{"0"} (for [v (vvals (val %)), v (vvals v)] (:i v))) xx)
([:ww {:xyz {:y3 {:x "abc", :i "ee"}, :y2 {:y "abc", :i "0"}, :y1 {:x "abc", :u "ee"}}}])
I am not able to run inner [:tr] without (do (html5 ..)) when i am using nested let statement having for loop.
(defpartial column-settings-layout [& content]
(html5
[:head
[:title "my-noir-proj"]
(include-css "assets/css/bootstrap.css")
]
[:body
[:div
[:div
[:image {:src "assets/img/ceva.gif" :alt "ceva-logo"}]
(toolbar)
]
[:div {:style "overflow: auto; overflow-x: hidden"}
content]
[:form {:id "col_settings_form" :name "col_settings_form" :method="post" :enctype="multipart/form-data"}
[:input {:type "button" :value "Save" :onclick "ajaxWithSerialize('save_cols_response_div','/save_cols_settings',$(col_settings_form).serialize());"}]
[:table {:class "table-striped" :border "1"}
[:tr [:td {:id "processing_status" } ][:td {:id "save_cols_response_div" :colspan 6} ]]
[:tr [:td ][:td {:colspan "3"} "SP" ] [:td {:colspan "3"} "WP"]]
(let [wp_settings (session/get :wp_settings)
sp_settings (session/get :sp_settings)]
(do (html5 [:tr [:td {:colspan "7"} "jhyghjghj"]]))
(for [col (:all_cols (session/get :setting_doc))]
(let
[
dest_station (keyword (session/get :dest_station))
;col_nm (:col_nm (nth col 1))
field_nm (nth col 0)
sp_col_nm (:col_nm (field_nm (dest_station sp_settings)))
wp_col_nm (:col_nm (field_nm (dest_station wp_settings)))
sp_editable (:editable (field_nm (dest_station sp_settings)))
wp_editable (:editable (field_nm (dest_station wp_settings)))
]
(do (html5 [:tr[:td "sfsdfgfds"]]
[:tr
[:th { :align "right" :class "input-small" } field_nm ]
[:td {:title sp_editable }[:input {:type "text" :class "input-large" :name (str "page_sp[" dest_station "][" field_nm "][col_nm]") :value sp_col_nm } ] ]
[:td [:input {:type "checkbox" :name (str "page_sp[" dest_station "][" field_nm "][col_nm]") :value field_nm}]]
[:td [:input {:type "checkbox" :name (str "page_sp[" dest_station "][" field_nm "][editable]") :value field_nm}]]
[:td {:title wp_editable }[:input {:type "text" :class "input-large" :name (str "page_wp[" dest_station "][" field_nm "][col_nm]") :value wp_col_nm} ] ]
[:td [:input {:type "checkbox" :name (str "page_wp[" dest_station "][" field_nm "][col_nm]") :value field_nm}]]
[:td [:input {:type "checkbox" :name (str "page_wp[" dest_station "][" field_nm "][editable]") :value field_nm}]]
]))
)
)
)
]
]
(footer)
;my includes of js and css
]]))
Your problem is likely that you're attempting to do something like
[:tr (for ... [:td .])]
which results in the invalid hiccup format
[:tr [[:td ..] [:td ..] ..]] ; note the vector of vectors inside the :tr
where hiccup expects
[:tr [:td ..] [:td ..] ..] ; :td vectors are direct elements of :tr
To get the expected formatting, you need something like
(into [:tr] (for ... [:td .]))
update: the reason your (html..) construct also fixes this problem is that it turns the whole sequence of formatted hiccup tags into a single HTML string. And you can drop the do - it's not doing anything useful.
You want to instead write
(html5
[:tr ...]
(for ...))
Or even just
(cons [:tr ...]
(for ...])
do is for side effects, and hiccup is purely functional, with no side effects. Instead of using do to group side effects, you use lists to group document elements.