If I have a collection of a particular shape:
["Alpha", "Beta", "Gamma"] ;; vector of strings
and I want to transform it by wrapping each element:
[{:name "Alpha"}, {:name "Beta"}, {:name "Gamma"}]
Is there a better way to express that than this rather kludgy map?
(map #(identity {:name %}) coll)
If you don't like map with (fn [v] {:name v}) you can use for:
(for [v coll] {:name v})
;; => ({:name "Alpha"} {:name "Beta"} {:name "Gamma"})
You could simply use fn:
(map (fn [v] {:name v}) coll)
if you want to use the anonymous function syntax you can use array-map to construct the map:
(map #(array-map :name %) coll)
Related
Let's say I have a reagent atom with a vector of maps like this:
(def my-atom (reagent/atom {:id 256
:name "some name"
:lines [{:code "ab43" :name "first nested name" :quantity 4}
{:code "bc22" :name "second nested name" :quantity 1}
{:code "lu32" :name "third nested name" :quantity 1}}] }))
How can I update the value of a key :quantity at a certain vector nested index, for example: update line with code "bc22" to 10 quantity.
This need to filter to get the index of vector, but haven't the index because filter by "code":
(swap! my-atom assoc-in [:lines 1 :quantity] 10)
I can find with filter, but I can't swap! quantity:
(->> (:lines #my-atom)
(filter #(= (:code %) "bc22")
first))
You can stick with the use of assoc-in but to do so, you have to retrieve the index associated to a given code from the vector of the :lines field in some way.
For example, I would a helper function:
(defn code->index [data code]
(->> data
:lines
(map-indexed (fn [i v] [i v]))
(filter (fn [[_ v]] (= (:code v) code)))
ffirst))
;; (code->index #my-atom "bc22")
;; => 1
And then use it in the swap:
(swap! my-atom assoc-in [:lines (code->index #my-atom "bc22") :quantity] 10)
(require
'[com.rpl.specter :as s])
(let [*a (atom {:id 256
:name "some name"
:lines [{:code "ab43" :name "first nested name" :quantity 4}
{:code "bc22" :name "second nested name" :quantity 1}
{:code "lu32" :name "third nested name" :quantity 1}]})]
(s/setval [s/ATOM :lines s/ALL #(-> % :code (= "bc22")) :quantity] 10 *a))
You've got options here. You could look up the index of the item, you could map over the list, updating only the item your interested in.
Depending on the specifics of the situation, you could also look at either storing the index of the element when the component is rendered, or instead build a set of cursors which are passed to your component. In that case you simply update he cursor like you would an atom, at it handles updating the backing atom efficiently.
Personally, I look at this and wonder if you are using the correct data structure in the first place. It seems probable that code is a natural key here, especially since you are looking to update a line based on it. Perhaps a map with code as the key and the full line as the value would make more sense. This also makes certain undesirable situations impossible (e.x. multiple lines with the same code). Of course you'd lose ordering (unless you re-established it somehow), which may or may not be an issue.
I make an http request and try to put the returned val into a reagent component like so:
[div
(a/take! (http/get "http://localhost:5000/data"))
#(into [:div]
(map render-method
(into [] (map (fn [res] (:name res)) (-> % :body :results))))
)
)
]
But this understandably doesn't work because the a/take! itself doesn't return the component. So how does one make the async get request work with reagent?
You can't do this. Instead you need to store the result in an atom, and reagent will rerender for you once it's loaded.
(def data (reagent/atom nil))
(defn fetch-data []
(take! (http/get "http://localhost:5000/data") #(reset! data %)))
(defn names-list []
[:div
(doall (map :name #data))])
(defn my-component []
[:div
(if #data
[names-list]
[:div "loading"])])
With Clojure's Core.Async one can map over a channel by using transducers:
(def my-chan (chan (buffer 10) (map inc)))
But what happens if the mapper function itself is async?
Say, we have a:
(defn async-inc [n]
(let [c (promise-chan)]
(put! (inc n))
c))
Is there a similar concise way to map the channel over this function? Or would one have to do something like this:
(def my-chan (chan (buffer 10)))
(def my-chan2 (chan (buffer 10)))
(go (while true
(>! my-chan2
(<! (async-inc (<! my-chan))))))
It would not really be mapping, since two channels are needed instead of one.
There is a general advice not to create channel inside a function and return it out because it forces user of that function to use core.async. You can either return an output through a promise or return through callback function.
Assuming what you want to do with an output from async-inc is to print it out using println function.
Return through a promise
(defn async-inc [n]
(let [p (promise)]
(deliver p (inc n))
p))
(println #(async-inc (<! my-chan)))
Return through callback
(defn async-inc [n callback]
(callback (inc n)))
(async-inc (<! my-chan) println)
But if you don't have a control over async-inc. Here are your options.
Use <!!
(println (<!! (go (<! (async-inc (<! my-chan))))))
or
Use take!
(take! (go (<! (async-inc (<! my-chan)))) println)
I know how to create mutable vector:
(defn create-vector []
(intern *ns* (symbol "my-vector" (ref []))
=>my-namespace/my-vector
I know how to add element to that vector:
(dosync (alter my-vector conj "test"))
=>["test"]
Now I have "test" string in my mutable vector. But how I can remove it? I tried to use lazy-sequence function remove
(dosync (alter my-vector remove "test"))
but it doesn't work. How can I remove element from mutable vector?
EDIT:
The collection doesn't really have to be vector. I found solution for set, but will wait if someone will suggest solution for vector.
your mistake is that you pass the "test" arg to remove function, while it really accepts a predicate. In your case this one wold work (unproperly though, making a list instead of a vector):
(dosync (alter my-vector #(remove #{"test"} %)))
to keep it a vector you would have to do this:
(dosync (alter my-vector #(vec (remove #{"test"} %))))
what makes me curious about your example:
why would you use this weird construction:
(defn create-vector []
(intern *ns* (symbol "my-vector" (ref []))))
instead of just (def my-vector (ref []))
why do you use ref at all? would you be using it inside some transaction? if not, i would propose you to move to an atom (since it is also thread safe)
user> (def my-atom (atom []))
#'user/my-atom
user> (swap! my-atom conj "hello")
["hello"]
user> (swap! my-atom #(remove #{"hello"} %))
()
As specified in my edit, collection doesn't have to be really vector. #leetwinski posted answer how to do it for vector, but if I would decide to use set I found other solution. So if we create set the same way as in question replacing [] to #{}:
(defn create-vector []
(intern *ns* (symbol "my-set" (ref #{}))
=>my-namespace/my-vector
and add element:
(dosync (alter my-set conj "test"))
=>#{"test"}
, we can remove it
(dosync (alter my-set disj item))
=>#{}
I have a Clojure function with parameter: [{:keys [from to]}]. Both the from and to keys are optional. I would like to remap these keys into new keys but maintaining the values. What's an efficient/idiomatic way to go about this? Here's my curent solution.
(defn query
[{:keys [from to]}]
(let [re-map {:$gte from
:$lt to}]
(into {} (remove #(nil? (val %)) re-map))))
I need it to return either one or both, and no nil if one of the key wasn't entered. So
=>(query {:from 10})
{:$gte 10}
and
=>(query {:from 10 :to 20})
{:$gte 10 :lt 20}
There is a function for this in the clojure.set namespace called rename-keys:
user=> (use 'clojure.set)
nil
user=> (rename-keys {:from 10} {:from :$gte :to :$lt})
{:$gte 10}
You can use destructuring for this:
user=> (defn re-map [{the-x :x the-y :y}] the-x)
user=> (re-map {:x 10 :y 1})
10
I would recommend this excellent introduction with lot of examples.