I have a vector of vectors [[plate,'p1',0,1],[plate,'p2',0,2],[plate,'p3',1,1]] containing x,y positions of detected plates.
How do I retrieve the x position of plate p3?
It seems to be a simple task but I'm more familiar with python, so I'm not sure how to do this in clojure.
i would go with something like this:
(def data [[:plate "p1" 0 1] [:plate "p2" 0 2] [:plate "p3" 1 1]])
(some (fn [[_ v x y]] (when (= v "p3") [x y])) data)
;;=> [1 1]
(some (fn [[_ v x y]] (when (= v "p123") [x y])) data)
;;=> nil
(def p '[[plate,"p1",0,1],[plate,"p2",0,2],[plate,"p3",1,1]])
;; be aware, 'p1' you can use in Python, but because in Clojure `'` at beginning
;; of a symbol is parsed as `quote`, you can't use `''` instead of `""` in contrast to Python.
;; create a nested map out of the vec of vecs
(defn vecs-to-map [vecs]
(reduce (fn [m [_ id x y]] (assoc m id {:x x :y y}))
{}
vecs))
(def m (vecs-to-map p))
;;=> {"p1" {:x 0, :y 1}, "p2" {:x 0, :y 2}, "p3" {:x 1, :y 1}}
;; you can access such a nested list via `get-in` and giving the nested map
;; and the keys it should apply on it.
(get-in m ["p3" :x])
;;=> 1
Since the irregularity that one key is a string and the other a keyword is
not so nice, I would make out of them all keywords:
(defn vecs-to-map [vecs]
(reduce (fn [m [_ id x y]] (assoc m (keyword id) {:x x :y y}))
{}
vecs))
(def m (vecs-to-map p))
;; access it by:
(get-in m [:p3 :x])
;;=> 1
Additional Thoughts
We ignored the first element of the vec plate.
Let's say there exist also another vectors like
(def b '[[box "b1" 0 1] [box "b2" 0 2] [box "b3" 1 1]])
And if we want a nested map which contains :plate and :box in the
outer level as keys, we have to change the vecs-to-map function.
(defn vecs-to-map [vecs]
(reduce (fn [m [k id x y]] (assoc m (keyword k)
(assoc (get m (keyword k) {})
(keyword id) {:x x :y y})))
{}
vecs))
Then we can generate the map containing everything by:
(def m (vecs-to-map (concat p b)))
;; or alternatively in two steps:
;; (def m (vecs-to-map p))
;; (def m (merge m (vecs-to-map b)))
m
;; => {:plate {:p1 {:x 0, :y 1}, :p2 {:x 0, :y 2}, :p3 {:x 1, :y 1}}, :box {:b1 {:x 0, :y 1}, :b2 {:x 0, :y 2}, :b3 {:x 1, :y 1}}}
And we access the content by:
;; access through:
(get-in m [:plate :p3 :x])
;; => 1
(get-in m [:box :b2 :y])
;; => 2
You don't really provide much context on what you're trying to do but it feels like you want to filter the vector of tuples to those that have the symbol p3' in the second position and then return just the third and fourth elements of such a match?
If so, the following would work:
dev=> (def plate :plate)
#'dev/plate
dev=> (def data [[plate,'p1',0,1],[plate,'p2',0,2],[plate,'p3',1,1]])
#'dev/data
dev=> (let [[[_ _ x y]] (filter (comp #{'p3'} second) data)]
#_=> [x y])
[1 1]
This doesn't feel very idiomatic, so perhaps you could explain more of the context?
Note: 'p3' is a symbol whose name is p3' so I wonder if you mean "p3" for a string?
The vector of vector format doesn't seem very conducive to the sort of access you want to perform - perhaps changing it to a hash map, whose keys are the plate IDs (if that's what p1, p2, and p3 are?) would be better to work with?
Edit: in response to #leetwinkski's note about the result when there is no match, here's an alternative:
You could use when-first:
dev=> (when-first [[_ _ x y] (filter (comp #{'p123'} second) data)]
#_=> [x y])
nil
dev=> (when-first [[_ _ x y] (filter (comp #{'p3'} second) data)]
#_=> [x y])
[1 1]
Here is how I would do it, based on my favorite template project. Please also note that in Clojure strings always use double-quotes like "p1". Single quotes are totally different!
(ns tst.demo.core
(:use tupelo.core tupelo.test))
(defn vec-has-label
"Returns true iff a vector has the indicated label"
[vec lbl]
(= lbl (nth vec 1)))
(defn vec->x
"Given a vector, return the x value"
[vec]
(nth vec 2))
(defn find-label
[matrix label]
(let [tgt-vecs (filterv #(vec-has-label % label) matrix) ; find all matching vectors
x-vals (mapv vec->x tgt-vecs)] ; extract the x values
x-vals))
The unit tests show the code in action
(dotest
(isnt (vec-has-label '[plate "p1" 0 1] "p0"))
(is (vec-has-label '[plate "p1" 0 1] "p1"))
(is= 9 (vec->x '[plate "p1" 9 1]))
(let [matrix (quote
[[plate "p1" 0 1]
[plate "p2" 0 2]
[plate "p3" 1 1]])]
(is= (find-label matrix "p3")
[1])
))
The unit test show the 2 ways of "quoting" a data structure that contains one or more symbols. This would be unneeded if the redundant plate symbol weren't present.
See also this list of documentation sources.
Destructuring maps, for example with {:keys [x y]} m, cannot be done in function paramters when there are going to be many maps (say m0, m1) of the same type. Here doing so would result in x and y being shadowed. So instead this is what I am doing for the point type:
(defn intersection [point0
point1]
(let [x0 (:x point0)
y0 (:y point0)
x1 (:x point1)
y1 (:y point1)]))
What is a cleaner (more idiomatic) way of achieving the above? I want a point to always have the keys :x and :y, rather than resorting to the first point having keys :x0 and :y0, and the second having :x1 and :y1.
same type = "consistent names in the input maps"
If I assume you are asking to have consistent names in the input maps, so every points has the keys :x and :y: then you can use the other syntax for map destructuring:
{name key-to-use ...}
the :keys form is a shortcut for this form.
If the points are passed as their own arguments:
user> (def p1 {:x 0 :y 1})
#'user/p1
user> (def p2 {:x -3 :y -8})
#'user/p2
user> (defn example [{x0 :x y0 :y} {x1 :x y1 :y}]
[x0 x1 y0 y1])
#'user/example
user> (example p1 p2)
[0 -3 1 -8]
If you are passing a list of points then you can nest map and list destructuring
user> (defn example [[{x0 :x y0 :y} {x1 :x y1 :y}]]
[x0 x1 y0 y1])
#'user/example
user> (example [p1 p2])
[0 -3 1 -8]
You can choose which names is bound to a key:
(let [{x1 :x y1 :y} m1
{x2 :x y2 :y} m2]
(foo x1 y1 x2 y2))
See also the following example from Destructuring in Clojure:
(def my-map {:a "A" :b "B" :c 3 :d 4})
(let [{a :a, x :x, :or {x "Not found!"}, :as all} my-map]
(println "I got" a "from" all)
(println "Where is x?" x))
;= I got A from {:a "A" :b "B" :c 3 :d 4}
;= Where is x? Not found!
I would like to translate the inner-function call in the following snippet, to one using the #() macro :
(let [m {:a 3, :b 2, :c 4, :x 9, :y 0, :z 5}]
(into (sorted-map-by (fn [key1 key2]
(compare [(get m key2)]
[(get m key1)]))) m))
I am a little bit confused on how I can accomplish that.
Inside an anonymous function, the arguments are given by %1, %2... so you can use
(let [m {:a 3, :b 2, :c 4, :x 9, :y 0, :z 5}]
(into (sorted-map-by #(compare (get m %2)
(get m %1))) m))
note you don't need to wrap the compared values in a vector.
I have a bunch of records (A B C) that implement a protocol P.
I want to write a method which will select one of the types of records, construct it, and then call a method on it.
For example, if I have a list of the records
(def types '(A B C)) I want to do something like (->(first types) 1 2 3)
Well, functions are also values and can be stored in and retrieved from data structures. So you should be able to just store the constructor functions you want to pick from in some convenient format and use them from there. Something like:
(defrecord foo [x y])
(defrecord bar [x y z])
(def constructors [->foo ->bar])
((first constructors) 4 5) ;;=> #user.foo{:x 4, :y 5}
;;or
(apply (second constructors) [20 true :baz]) ;;=> #user.bar{:x 20, :y true, :z :baz}
(-> (second constructors) (apply '(59 -30 false))) ;;=> #user.bar{:x 59, :y -30, :z false}
or you can even skip the data structure entirely:
(defn quxx [n a-map]
((if (< 25 n) map->foo map->bar) a-map))
(quxx 2 {:x 3 :y 9 :z -200}) ;;=> #user.bar{:x 3, :y 9, :z -200}
(quxx 29 {:x 3 :y 9 :z -200}) ;;=> #user.foo{:x 3, :y 9, :z -200}
What is the idiomatic way of conj-ing a list of values to a map value?
This is the result I want, but the anonymous function looks kind of ugly imo. Is there a better way?
> (update-in {:x #{}} [:x] #(apply conj % '(1 2)))
{:x #{1 2}}
The anonymous function is unnecessary
(update-in {:x #{}} [:x] conj 1 2)
;=> {:x #{1 2}}
(update-in {:x #{}} [:x] into [1 2])
;=> {:x #{1 2}}
You should not have to know whether the map contains? the key that you are conjing values to. Adapting your example ...
(update-in {} [:x] #(apply conj % '(1 2)))
;{:x (2 1)}
... not what you want.
The following
(defn assocs [m k coll]
(assoc m k (into (get m k #{}) coll)))
... supplies an empty-set value if no entry for the key exists.
(assocs {} :x [1 2])
;{:x #{1 2}}
(assocs {:x #{2}} :x [1 2])
;{:x #{1 2}}
You'll find similar code in clojure.algo.graph, for example here. (Warning: the graph type only functions in one of the algorithms and otherwise just gets in the way.)