After making a select supposed, the clojure.java.jdbc returns me something like this:
({:name "John"} {:name "Julia"} {:name "Alex"})
I need to transform this return into a vector, to look like this:
["John" "Julia" "Alex"]
How can I do this? I can't think of a way to make this transformation
To get the data in the exact shape you described:
You can use keywords as functions to extract that exact value from a map
You can use the function mapv to call a function on each element of a sequence and get a vector back (eagerly)
E.g.
user=> (mapv :name '({:name "John"} {:name "Julia"} {:name "Alex"}))
["John" "Julia" "Alex"]
(def data [{:name "John"} {:name "Julia"} {:name "Alex"}])
(mapv :name data)
;=> ["John" "Julia" "Alex"]
Please see also this list of documentation resources.
Related
I just started learning Clojure and I'd like to get two keywords from a vector of maps.
Let's say there's a vector
(def a [{:id 1, :description "bla", :amount 12, :type "A", :other "x"} {:id 2, :description "blabla", :amount 10, :type "B", :other "y"}])
And I'd like to get a new vector
[{"bla" 12} {"blabla" 10}]
How can I do that??
Thanks!
Assuming you want the :description and :amount separately, not maps that map one to the other, you can use juxt to retrieve both at the same time:
(mapv (juxt :description :amount) a)
;; => [["bla" 12] ["blabla" 10]]
If you actually did mean to make maps, you can use for instance apply and hash-map to do that:
(mapv #(apply hash-map ((juxt :description :amount) %)) a)
;; => [{"bla" 12} {"blabla" 10}]
You can use mapv to map over the source vector. Within the transform function you can destructure each map to extract the keys you want and construct the result:
(mapv (fn [{:keys [description amount]}] {description amount}) a)
(mapv #(hash-map (:description %) (:amount %)) a)
Is there a simple way in specter to collect all the structure satisfying a predicate ?
(./pull '[com.rpl/specter "1.0.0"])
(use 'com.rpl.specter)
(def data {:items [{:name "Washing machine"
:subparts [{:name "Ballast" :weight 1}
{:name "Hull" :weight 2}]}]})
(reduce + (select [(walker :weight) :weight] data))
;=> 3
(select [(walker :name) :name] data)
;=> ["Washing machine"]
How can we get all the value for :name, including ["Ballast" "Hull"] ?
Here's one way, using recursive-path and stay-then-continue to do the real work. (If you omit the final :name from the path argument to select, you'll get the full “item / part maps” rather than just the :name strings.)
(def data
{:items [{:name "Washing machine"
:subparts [{:name "Ballast" :weight 1}
{:name "Hull" :weight 2}]}]})
(specter/select
[(specter/recursive-path [] p
[(specter/walker :name) (specter/stay-then-continue [:subparts p])])
:name]
data)
;= ["Washing machine" "Ballast" "Hull"]
Update: In answer to the comment below, here's a version of the above the descends into arbitrary branches of the tree, as opposed to only descending into the :subparts branch of any given node, excluding :name (which is the key whose values in the tree we want to extract and should not itself be viewed as a branching off point):
(specter/select
[(specter/recursive-path [] p
[(specter/walker :name)
(specter/stay-then-continue
[(specter/filterer #(not= :name (key %)))
(specter/walker :name)
p])])
:name]
;; adding the key `:subparts` with the value [{:name "Foo"}]
;; to the "Washing machine" map to exercise the new descent strategy
(assoc-in data [:items 0 :subparts2] [{:name "Foo"}]))
;= ["Washing machine" "Ballast" "Hull" "Foo"]
The selected? selector can be used to collect structures for which another selector matches something within the structure
From the examples at https://github.com/nathanmarz/specter/wiki/List-of-Navigators#selected
=> (select [ALL (selected? [(must :a) even?])] [{:a 0} {:a 1} {:a 2} {:a 3}])
[{:a 0} {:a 2}]
I think you could iterate on map recursively using clojure.walk package. On each step, you may check the current value for a predicate and push it into an atom to collect the result.
I have a reagent atom which consists a vector of key/value maps.
How can I remove a key/value pair with certain key value which happens to be UUID? I have tried:
(swap! state/items (dissoc #state/items id))
but I get:
core.cljs:270 Uncaught Error: No protocol method IMap.-dissoc defined for type cljs.core/LazySeq: ({:id #uuid "e1f9341f-bc02-4c17-a594-b9b5ede72214", :description "foo bar"})
I think you need to use remove. So this should help:
(def data [{:id "e1f9341f-bc02-4c17-a594-b9b5ede72214" :description "Foo"} {:id "e1f9341f-bc02-4c17-a594-b9b5ede72214" :description "Bar"}] )
(remove #(= (:id %) "e1f9341f-bc02-4c17-a594-b9b5ede72214") data)
;; => ()
The swap! function takes the old value and returns the updated value. So data from above will be the old value. Your function you pass to swap! will thus look like this:
(fn [old]
(remove #(= (:id %) "e1f9341f-bc02-4c17-a594-b9b5ede72214") old))
I am learning clojure and trying to implement a problem. I am storing maps in a vector. Each map contains an id. For example [{:id 1 :name "abc"} {:id 2 :name "xyz"}]. The map also contains some more fields.
I read somewhere that, instead of using a vector to store the maps, I could use an array-map and do away with my id and store it something like {1 {:name "abc"}, 2 {:name "xyz"}}.
I tried going through the clojure docs but didn't find a good example to achieve this. Can some please help me out and give me a good example?
You can use assoc to add values to a map. assoc takes 3 args. The first arg is the map that you want to add to, 2nd arg is a key, and the third is a value. The function returns the old map with the key-value pair added.
Example:
(assoc {} 1 {:name "abc"})
returns
{1 {:name "abc"}}
Your idea is to lift the :id entry of each record-map into an index, while removing it from the map. You end up with a map of :id-less records instead of a vector of full records.
The following function lifts the key fk out of the collection of maps ms:
(defn key-by [fk ms]
(into {} (map (fn [m] [(get m fk) (dissoc m fk)]) ms)))
For example,
(key-by :id [{:id 1 :name "abc"} {:id 2 :name "xyz"}])
;{1 {:name "abc"}, 2 {:name "xyz"}}
Note:
Every record should have an :id.
Your :ids had better be distinct, or you'll lose records.
Don't depend on array-map: it's an implementation detail. A
modified version might well be a hash-map.
If you need your map sorted by key, use a sorted-map.
If you need to keep your records in insertion order, think again.
I have 2 data structures like the ones below
(ns test)
(def l
[{:name "Sean" :age 27}
{:name "Ross" :age 27}
{:name "Brian" :age 22}])
(def r
[{:owner "Sean" :item "Beer" }
{:owner "Sean" :item "Pizza"}
{:owner "Ross" :item "Computer"}
{:owner "Matt" :item "Bike"}])
I want to have get persons who dont own any item . (Brian in this case so [ {:name "Brian" :age 22}]
If this was SQL I would do left outer join or not exists but I not sure how to do this in clojure in more performant way.
While Chuck's solution is certainly the most sensible one, I find it interesting that it is possible to write a solution in terms of relational algebraic operators using clojure.set:
(require '[clojure.set :as set])
(set/difference (set l)
(set/project (set/join r l {:owner :name})
#{:name :age}))
; => #{{:name "Brian", :age 22}}
You basically want to do a filter on l, but negative. We could just not the condition, but the remove function already does this for us. So something like:
(let [owner-names (set (map :owner r))]
(remove #(owner-names (% :name)) l))
(I think it reads more nicely with the set, but if you want to avoid allocating the set, you can just do (remove (fn [person] (some #(= (% :owner) (person :name)) r)) l).)