This is pretty much a follow-up to my last question (Clojure idiomatic way to update multiple values of map), but not quite the same. (keep in mind that I'm fairly new to Clojure and functional languages alike)
suppose I have the following data structure, defined as a map of sets:
(def m1 {:1 #{2} :2 #{1 3} :3 #{1}})
and a map of maps as such:
(def m2 {:1 {:1 0 :2 12 :3 23} :2 {:1 23 :2 0 :3 4} :3 {:1 2 :2 4 :3 0}})
What I want to do is update the registries of m2 that have a correspondence in m1 to a certain value. Let's say the value I want is x. The resulting m2 would be something like this:
{:1 {:1 0 :2 x :3 23} :2 {:1 x :2 0 :3 x} :3 {:1 x :2 4 :3 0}}
Assuming that v contains every possible key for my map, y first attempt, (that I failed miserably) is to do something like this: (assume that x=1
(for [i v]
reduce (fn [m j] (assoc-in m [i j] 1)) d (i m1)))
needless to say that it was a failure. So, how is the idiomatic way to do this?
As I understand your requirements you want to
Produce a number of key-sequences from m1.
In m2 associate each of those key-sequences with a particular constant value.
The first step is a fairly simple transformation of m1. We want to produce 0 or more key-seqs for each entry (depending on how many are in its set), so the natural go-to for me is mapcat. It stands for map-then-concat and is great for just such a case where from each element of a seq we produce 0 or more of the elements we want.
(defn key-seqs [coll]
(mapcat
(fn [[k v]]
(map (partial vector k) v))
coll))
(key-seqs m1)
;;=> ([:1 2] [:2 1] [:2 3] [:3 1])
Note that the function taken by mapcat is itself a map, so that it produces a sequence (possibly empty) for mapcat to unroll. But this is returning the longs stored in the sets as themselves. If we want to turn them into keywords to match m2 we need a little more processing:
(defn key-seqs [coll]
(mapcat
(fn [[k v]]
(map (comp (partial vector k) keyword str) v))
coll))
(key-seqs m1)
;;=> ([:1 :2] [:2 :1] [:2 :3] [:3 :1])
(We need to use str because keyword doesn't know what to do with longs. Normally keywords are not numbers, but names with some symbolic meaning)
Then we can adapt the update-m from your previous question a little bit so that it can take the constant value as an argument and handle key-sequences that don't just have the same value twice:
(defn update-m [m x v]
(reduce (fn [m' key-seq]
(assoc-in m' key-seq x)) ;; accumulate changes
m ;; initial-value
v)) ;; collection to loop over
and now we seem to be in business:
(update-m m2 1 (key-seqs m1))
;;=> {:1 {:1 0, :2 1, :3 23}, :2 {:1 1, :2 0, :3 1}, :3 {:1 1, :2 4, :3 0}}
I think a nice solution would be, if you change the data structure of m1 to something like
(def m1-new [[:1 :2] [:2 :1] [:2 :3] [:3 :1]])
Then you can just reduce over it and use assoc-in.
(reduce (fn [m path] (assoc-in m path my-new-value)) m2 m1-new)
Try this (here x is 100)
(merge-with merge m2
(into {} (for [[k v] m1] [k (into {} (for [i v] [(keyword (str i)) 100]))])))
EDIT:
The idea is this:
Convert m1 from {:1 #{2} :2 #{1 3} :3 #{1}} to {:1 {:2 x} :2 {:1 x :3 x} :3 {:1 x}} which is basically converting each set to a map where key is the values of the set and values are the constant x.
Merge both the m2 and new m1.
NOTE: There is an assumption that all the keys in m1 are there in m2.
Related
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.
Is there a way to use a single list comp to achieve the following or do I need some other method?
(def args '([[:db/foo 1 2] [:db/bar 3 4]] {:a 1 :b 2}))
(defn myfunc []
"Want to run inner loop only once if arg is a map."
(for [arg args [cmd & params] arg] ; 'cmd & params' are meaningless if map.
(if (map? arg)
arg
(into [cmd] params))))
Above code produces
=> [[:db/foo 1 2] [:db/bar 3 4] {:a 1, :b 2} {:a 1, :b 2}]
But I actually want
=> [[:db/foo 1 2] [:db/bar 3 4] {:a 1, :b 2}]
Obviously this isn't the full function, I'm doing transforms if the arg is in vector form but want to let map form pass straight through (without being duplicated).
Update: I've found that a list comprehension has its roots in set builder notation, whereas 'conditionally executing the inner loop' is actually an imperative notion, so it's not surprising this isn't easy to express with a single list comprehension, which is a totally fp construct.
Is this what you want to do?:
Build a new sequence from an existing sequence. For every element in the input sequence, there are two possible cases:
It is a map: In that case, just append it to the result sequence
Otherwise it is a sequence and its elements should possibly be transformed and appended to the result sequence
If this is what you want to do, this computation can conveniently be expressed by first mapping the elements of the input sequence to subsequences and then concatenating those sequences to form the resulting sequence.
As suggested by #cfrick, there is mapcat that combines mapping with concatenation, either by passing in a sequence directly to mapcat
(mapcat (fn [x] (if (map? x) [x] x)) args)
or by using mapcat as a transducer
(into [] (mapcat (fn [x] (if (map? x) [x] x))) args)
I don't think for is suitable for expressing this computation.
before I propose a solution, I have a few remarks on the function myfunc that you provided:
the doc string is misplaced. I know it's weird but the doc string must be before the arguments of a function, so before [],
your usage of for instruction is not what you expect I think. You should take a look on the example in the documentation here. Mainly the for instruction is nice to construct Cartesian product from list.
I hope that I understood correctly what you want to achieve. In my proposition, I use a recursion through loop instruction to build a vector which will contain every arguments:
(defn myfunc "Want to run inner loop only once if arg is a map."
[& args]
(loop [arg-to-parse args
res []]
(if (empty? arg-to-parse)
res
(let [arg (first arg-to-parse)
new-arg-to-parse (rest arg-to-parse)]
(if (map? arg)
(recur new-arg-to-parse (conj res arg))
(recur new-arg-to-parse (into res arg))
)))))
(myfunc [[:db/foo 1 2] [:db/bar 3 4]] {:a 1 :b 2})
;; => [[:db/foo 1 2] [:db/bar 3 4] {:a 1, :b 2}]
(myfunc {:a 1 :b 2} [[:db/foo 1 2] [:db/bar 3 4]])
;; => [{:a 1, :b 2} [:db/foo 1 2] [:db/bar 3 4]]
I am totally new to clojure.
I have a JSON like: { "1": true, "2": false, "3": true, "4": false }
I want to create an array of keys for which the value is true in clojure. In this example the array should be ["1", "3"].
Please help me. Any help would be appreciated.
there are also couple of short and simple snippets for that:
user> (filter m (keys m))
;;=> ("1" "3")
user> (keep (fn [[k v]] (when v k)) m)
;;=> ("1" "3")
user> (for [[k v] m :when v] k)
;;=> ("1" "3")
If you're fine with using a vector instead of an array (since you're usually using vectors in Clojure anyway), you can do something like.
(defn keys-for-truthy-vals [m]
(->> m (filter val) (mapv key)))
Note The mapv is only so the map call returns a vector. If you want a seq, just use map.
The same as already provided, just staying in maps.
(keys (filter val m))
If your map is a Something like (->> (filter (fn [[k v]] v) a) (map (fn [[k v]] k))) will work. You can't do it with just a map because you need to drop certain values, so there will need to be some reducing or filtering.
There is built-in function in the Tupelo library for this:
(submap-by-vals map-arg keep-vals & opts)
Returns a new map containing entries with the specified vals. Throws for missing vals,
unless `:missing-ok` is specified. Usage:
(submap-by-vals {:a 1 :b 2 :A 1} #{1 } ) => {:a 1 :A 1}
(submap-by-vals {:a 1 :b 2 :A 1} #{1 9} :missing-ok ) => {:a 1 :A 1}
You could then just use the keys function on the resulting map.
Maybe this?
(->> foo (filter second) keys)
where foo is a map.
I have a function that takes three arguments say somefunction [param1 param2 param3] and a vector with values say [val1 val2 val3 val4 ...]
I would like to repeatedly call the somefunction with the first argument param1 as fixed and the other two parameters passed in the combination val1 val2, val2 val3, val3 val4 ... etc. which is equivalent to (somefunction sampleparam1 val1 val2) (somefunction sampleparam1 val2 val3) (somefunction sampleparam1 val3 val4)... etc. IS there a way to elegantly do this in clojure?
I'm not sure what you want to do with the results of each call to somefunction so I'll assume it is executed for its side-effects and use doseq.
(let [f (partial somefunction param1)]
(doseq [args (partition 2 1 [val1 val2 val3 val4])]
(apply f args)))
clojure.core/partition:
(partition 2 1 [1 2 3 4]) ;; ((1 2) (2 3) (3 4))
clojure.core/partial
(def plus10 (partial + 10))
(plus10 5) ;; 15
clojure.core/apply
(apply + [1 2 3]) ;; 6
Note: you can get away without using partial since apply accepts intervening arguments: (apply somefunction param1 args)
Vinoth, there is:
You are first asking to create a function that takes a maximum of three (3) arguments: [param1 param2 param3] but you want to be able to fix the first parameter. In addition, while you have stated that you have a vector of arguments it appears, from your write up, that you want to go through the vector of values such that the first call to somefunction takes the 1st and 2nd item from the vector, the second call takes the 2nd and 3rd item from the vector, and so on until the vector is exhausted.
The first part of your question (fixed first parameter) can be solved with:
(partial somefunction sampleparam1)
The second part is a bit more complicated and without more details I can only guess. But here is a small demonstration of one approach:
(defn somefunction
[param1 param2 param3]
(+ param1 param2 param3))
(def part (partial somefunction 100))
(let [x [1 2 3 4 5]
y (first x)
z (second x)]
(part y z))
If you could explain more about
Are the value vectors consistent in length?
What does somefunction do?
My first thought went to using reduce or loop but I hesitate to assume too much.
I think I get the gist of your question.
;; a function that returns the vector as pairs of sub-vectors
(defn pairs [v]
(map vector v (rest v)))
This splits your vector [val1 val2 val3...] into a sequence of pairs as you wanted.
Example output from this is
(pairs [1 2 3])
=> ([1 2] [2 3])
As #Kyle has pointed out you can also use (partition 2 1 v) here too, as you're only really interesting in sequences, not actual vectors as they get squashed later into parameters via apply.
Next, your function needs to do something. For illustration in the output, I'll just return a map with params as values to the keys :a :b :c
(defn somefn [p1 p2 p3]
{:a p1 :b p2 :c p3})
now create a new function that calls your function, but with a constant first arg. I'll just use :p1 as a marker. Thus you only have to call (partial-somefn p2 p3) which will call (somefn :p1 p2 p3)
(def partial-somefn (partial somefn :p1))
then to call it with pairs from your vector (or sequence)...
either use map...
(map #(apply partial-somefn %) (pairs [1 2 3]))
=> ({:b 1, :c 2, :a :p1} {:b 2, :c 3, :a :p1})
or use doseq for side effects...
(doseq [ps (pairs [1 2 3])]
(apply partial-somefn ps))
or a for loop
(for [ps (pairs [1 2 3])]
(apply partial-somefn ps))
=> ({:b 1, :c 2, :a :p1} {:b 2, :c 3, :a :p1})
You can see that the maps returned show the parameters were called in sequence with the constant first parameter.
So the condensed version is
(defn somefn [p1 p2 p3]
;; your code here
)
(def v [:v1 :v2 :v3])
(let [pairs (fn [v] (map vector v (rest v)))
pf (partial somefn :param1)]
(map #(apply pf %) (pairs v)))
or use one of the for or doseq variations depending on what somefn does
Just started learning Clojure, so I imagine my main issue is I don't know how to formulate the problem correctly to find an existing solution. I have a map:
{[0 1 "a"] 2, [0 1 "b"] 1, [1 1 "a"] 1}
and I'd like to "transform" it to:
{[0 1] "a", [1 1] "a"}
i.e. use the two first elements of the composite key as they new key and the third element as the value for the key-value pair that had the highest value in the original map.
I can easily create a new map structure:
=> (into {} (for [[[x y z] v] {[0 1 "a"] 2, [0 1 "b"] 1, [1 1 "a"] 1}] [[x y] {z v}]))
{[0 1] {"b" 1}, [1 1] {"a" 1}}
but into accepts no predicates so last one wins. I also experimented with :let and merge-with but can't seem to correctly refer to the map, eliminate the unwanted pairs or replace values of the map while processing.
You can do this by threading together a series of sequence transformations.
(->> data
(group-by #(->> % key (take 2)))
vals
(map (comp first first (partial sort-by (comp - val))))
(map (juxt #(subvec % 0 2) #(% 2)))
(into {}))
;{[0 1] "a", [1 1] "a"}
... where
(def data {[0 1 "a"] 2, [0 1 "b"] 1, [1 1 "a"] 1})
You build up the solution line by line. I recommend you follow in the footsteps of the construction, starting with ...
(->> data
(group-by #(->> % key (take 2)))
;{(0 1) [[[0 1 "a"] 2] [[0 1 "b"] 1]], (1 1) [[[1 1 "a"] 1]]}
Stacking up layers of (lazy) sequences can run fairly slowly, but the transducers available in Clojure 1.7 will allow you to write faster code in this idiom, as seen in this excellent answer.
Into tends to be most useful when you just need to take a seq of values and with no additional transformation construct a result from it using only conj. Anything else where you are performing construction tends to be better suited by preprocessing such as sorting, or by a reduction which allows you to perform accumulator introspection such as you want here.
First of all we have to be able to compare two strings..
(defn greater? [^String a ^String b]
(> (.compareTo a b) 0))
Now we can write a transformation that compares the current value in the accumulator to the "next" value and keeps the maximum. -> used somewhat gratuitusly to make the update function more readable.
(defn transform [input]
(-> (fn [acc [[x y z] _]] ;; take the acc, [k, v], destructure k discard v
(let [key [x y]] ;; construct key into accumulator
(if-let [v (acc key)] ;; if the key is set
(if (greater? z v) ;; and z (the new val) is greater
(assoc acc key z) ;; then update
acc) ;; else do nothing
(assoc acc key z)))) ;; else update
(reduce {} input))) ;; do that over all [k, v]s from empty acc
user> (def m {[0 1 "a"] 2, [0 1 "b"] 1, [1 1 "a"] 1})
#'user/m
user> (->> m
keys
sort
reverse
(mapcat (fn [x]
(vector (-> x butlast vec)
(last x))))
(apply sorted-map))
;=> {[0 1] "a", [1 1] "a"}