Changing nested element of an atom - clojure

I am trying to change value of an atom:
#atom[{{:other-site-conf 2499/2500, :own-unconf|other-conf [[30.752499999999998 1.0126843266559153 12] "Humidity"], :other-site-unconf 1/2500, :both-unconf [[27.563987107797804 4.984857204039122 7511] "Humidity"], :site-id 1, :other-site-quant 0.86235090749618} {:other-site-conf 2497/2500, :own-unconf|other-conf [[22.976875 0.09445843442135488 4] "Temperature"], :other-site-unconf 3/2500, :both-unconf [[20.920551354838697 1.0618445458730512 7511] "Temperature"], :site-id 0, :other-site-quant 0.7673990631272951}} 0x5bf8ef86]
In this I want to change map values inside :both-unconf of second map that is of :site-id 0
I have tried using:
(swap! prob-cond update-in [1 1 :both-unconf 1 1 0] 22)
but not working.

You need to pass swap! a function.
(swap! prob-cond #(update-in % [1 1 :both-unconf 1 1 0] (constantly 22))

You have made a mistake with your data, I believe:
(def data
{{:other-site-conf 2499/2500,
:own-unconf|other-conf [[30.752499999999998 1.0126843266559153 12] "Humidity"],
:other-site-unconf 1/2500,
:both-unconf [[27.563987107797804 4.984857204039122 7511] "Humidity"],
:site-id 1,
:other-site-quant 0.86235090749618}
{:other-site-conf 2497/2500,
:own-unconf|other-conf [[22.976875 0.09445843442135488 4] "Temperature"],
:other-site-unconf 3/2500,
:both-unconf [[20.920551354838697 1.0618445458730512 7511] "Temperature"],
:site-id 0,
:other-site-quant 0.7673990631272951}}
)
Here data is a map with a single MapEntry. Both the key and value of this MapEntry are, in turn maps. This is almost certainly not what you wanted.
I am assuming you wanted to have a vector containing 2 maps, like so:
(def data
[{:other-site-conf 2499/2500,
:own-unconf|other-conf [[30.752499999999998 1.0126843266559153 12] "Humidity"],
:other-site-unconf 1/2500,
:both-unconf [[27.563987107797804 4.984857204039122 7511] "Humidity"],
:site-id 1,
:other-site-quant 0.86235090749618}
{:other-site-conf 2497/2500,
:own-unconf|other-conf [[22.976875 0.09445843442135488 4] "Temperature"],
:other-site-unconf 3/2500,
:both-unconf [[20.920551354838697 1.0618445458730512 7511] "Temperature"],
:site-id 0,
:other-site-quant 0.7673990631272951}
])
You can change the value like so:
(let [state (atom data)
result (swap! state #(assoc-in % [1 :both-unconf 0 0] 42)) ]
with result:
[{:other-site-conf 2499/2500,
:own-unconf|other-conf
[[30.752499999999998 1.0126843266559153 12] "Humidity"],
:other-site-unconf 1/2500,
:both-unconf
[[27.563987107797804 4.984857204039122 7511] "Humidity"],
:site-id 1,
:other-site-quant 0.86235090749618}
{:other-site-conf 2497/2500,
:own-unconf|other-conf
[[22.976875 0.09445843442135488 4] "Temperature"],
:other-site-unconf 3/2500,
:both-unconf [[42 1.0618445458730512 7511] "Temperature"],
:site-id 0,
:other-site-quant 0.7673990631272951}]

This is because your top level data structure is a map. So when you do assoc-in (or update-in) with [1 ...] it will create/update the map entry with key=1.
| | ks=> [1]
| (assoc-in {:a :b} ks 42)=> {:a :b, 1 42}
| | ks=> [1 :both-unconf]
| (assoc-in {:a :b} ks 42)=> {:a :b, 1 {:both-unconf 42}}
| | ks=> [1 :both-unconf 0]
| (assoc-in {:a :b} ks 42)=> {:a :b, 1 {:both-unconf {0 42}}}
| | ks=> [1 :both-unconf 0 0]
| (assoc-in {:a :b} ks 42)=> {:a :b, 1 {:both-unconf {0 {0 42}}}}
Now change your top level structure into a vector, with the second element as a map:
[:a {:both-unconf [[41 43]]}]
| | ks=> [1]
| (assoc-in [:a #] ks 42)=> [:a 42]
| | ks=> [1 :both-unconf]
| (assoc-in [:a #] ks 42)=> [:a {:both-unconf 42}]
| | ks=> [1 :both-unconf 0]
| (assoc-in [:a #] ks 42)=> [:a {:both-unconf [42]}]
| | ks=> [1 :both-unconf 0 0]
| (assoc-in [:a #] ks 42)=> [:a {:both-unconf [[42 43]]}]

I have obtained the required output by:
(reset! prob-cond (first #prob-cond))
(swap! prob-cond #(update-in % [1 :both-unconf 0 0] (constantly 22)))

Related

clojure-spec: Unable to get function's postcondition right

I'm trying clojure spec on a simple function that computes the "neighbours" of a (row,col) position in a square matrix. For example for the 4x4 matrix given below, the neighbours of cell (1,1) shall be: (0,1), (1,0), (1,2), (2,1). The neighbours of cell (4,3), which is not even in the matrix' range, shall be (3,3) etc.
The function's input is the size of the matrix and the (row,col) of the position of interest. The output is a collection of (row,col) of the neighbours. This collection can be empty if there are no neighbours.
This problem can be found in "The Joy of Clojure, 2nd editions, page 94; but this code is modified because the original was too compact for me. Then I tried to spec it and check the spec in the :pre and :post parts.
However, I don't get the :post part to work. When I run the test cases, I get:
java.lang.ClassCastException: java.lang.Boolean cannot be cast
to clojure.lang.IFn
What to change?
(require '[clojure.spec.alpha :as s]
'[clojure.test :as t])
; ===
; Specs
; ===
(s/def ::be-row-col
(s/coll-of integer? :count 2 :kind sequential?))
(s/def ::be-square-matrix-size
(s/and integer? #(<= 0 %)))
(s/def ::be-row-col-vector
(s/and (s/coll-of ::be-row-col) (s/int-in-range? 0 5 #(count %))))
; ===
; Function of interest
; ===
(defn neighbors [sqmsz rc]
{:pre [(s/valid? ::be-row-col rc)
(s/valid? ::be-square-matrix-size sqmsz)]
:post [(s/valid? ::be-row-col-vector %)]
}
(let [ cross [[-1 0] [1 0] [0 -1] [0 1]]
in-sq-matrix? (fn [x]
(and (<= 0 x) (< x sqmsz)))
in-sq-matrix-rc? (fn [rc]
(every? in-sq-matrix? rc))
add-two-rc (fn [rc1 rc2]
(vec (map + rc1 rc2)))
get-rc-neighbors (fn [rc]
(map (partial add-two-rc rc) cross)) ]
(filter in-sq-matrix-rc? (get-rc-neighbors rc))))
; ===
; Put a collection of [row col] into an expected form
; ===
; this is used to run the test code
(defn formify [rc-coll]
(let [ cmp (fn [rc1 rc2]
(let [ [r1 c1] rc1
[r2 c2] rc2 ]
(cond (< r1 r2) -1 ; sort by row
(> r1 r2) +1
(< c1 c2) -1 ; then by column
(> c1 c2) +1
true 0))) ]
(vec (sort cmp rc-coll))))
; ===
; Testing
; ===
(defn test-nb [ sqmsz rc expected txt ]
(do
(t/is (= (formify (neighbors sqmsz rc)) expected) txt)
))
(test-nb 0 [0 0] [] "Zero-size matrix, outside #1")
(test-nb 0 [1 1] [] "Zero-size matrix, outside #2")
(test-nb 1 [0 0] [] "One-size matrix, inside")
(test-nb 1 [1 0] [[0 0]] "One-size matrix, outside")
(test-nb 5 [0 0] [[0 1] [1 0]] "Testing top left")
(test-nb 5 [1 0] [[0 0] [1 1] [2 0]] "Testing left edge")
(test-nb 5 [1 1] [[0 1] [1 0] [1 2] [2 1]] "Testing middle #1")
(test-nb 5 [2 2] [[1 2] [2 1] [2 3] [3 2]] "Testing middle #2")
(test-nb 5 [3 3] [[2 3] [3 2] [3 4] [4 3]] "Testing middle #3")
(test-nb 5 [4 4] [[3 4] [4 3]] "Testing btm right")
(test-nb 5 [5 5] [] "Testing outside #1")
(test-nb 5 [5 4] [[4 4]] "Testing outside #2")
(test-nb 5 [4 3] [[3 3] [4 2] [4 4]] "Testing btm edge")
You're just missing the # prefix to make your anonymous function in the :post condition. The post condition needs to be a function that can take the output of the subject function's invocation.
:post [#(s/valid? ::be-row-col-vector %)]
Could also be rewritten as:
:post [(fn [o] (s/valid? ::be-row-col-vector o))]
But depending on your use case, you may want to look into function specs and instrument as an alternative to :pre and :post conditions. I wrote more examples here.

How to convert vector [1 2 3 :a :b :c :A :B :C] to [ {:index 1 :lower :a :upper :A} {:index 2 :lower :b :upper :B} {:index 3 :lower :c :upper :C} ]?

I want to know how to convert
vector [1 2 3 :a :b :c :A :B :C]
to
[ {:index 1 :lower :a :upper :A} {:index 2 :lower :b :upper :B} {:index 3 :lower :c :upper :C} ] ?
the vector may be [1 2 3 4 :a :b :c :d :A :B :C :D]
or if there is not an easy way,is there a way to convert
[{:index 1} {:index 2} {:index 3}] [{:lower :a} {:lower :b} {:lower :c}] [{:upper :A} {:upper :B} {:upper :C}]
to
[{:index 1 :lower :a :upper :A} {:index 2 :lower :b :upper :B} {:index 3 :lower :c :upper :C}]
Thanks!
So in general, when faced with a problem like this, I would go upstream and fix the input format. A vector that is a concatenation of arbitrary parts doesn't make any sense. For the sake of an answer, let us assume this isn't possible.
First we define a helper function to create the result maps:
(defn make-result [i l u]
{:index i :lower l :upper u})
Then we just need to map this function over the three subsequences:
(defn input->output [i]
(apply map make-result (partition (/ (count i) 3) i)))
We need to use apply as we generate a sequence of subsequences that we want to use as the parameters for map (recall that the function arity should match the number of sequences you pass to map - which conveniently our helper does).
This will work for both of the vectors given above.
(input->output [1 2 3 :a :b :c :A :B :C])
({:index 1, :lower :a, :upper :A} {:index 2, :lower :b, :upper :B} {:index 3, :lower :c, :upper :C})
(input->output [1 2 3 4 :a :b :c :d :A :B :C :D])
({:index 1, :lower :a, :upper :A} {:index 2, :lower :b, :upper :B} {:index 3, :lower :c, :upper :C} {:index 4, :lower :d, :upper :D})
Behaviour if the vector is in a different format may surprise or disappoint - perhaps some input validation is in order.
(let [ks [:index :lower :upper]
xs [1 2 3 :a :b :c :A :B :C]]
(->> xs
(partition (/ (count xs) (count ks)))
(apply map vector)
(mapv zipmap (repeat ks))))
How it works:
We first partition the vector by count:
(partition (/ (count xs) (count ks)) xs)=> ((1 2 3) (:a :b :c) (:A :B :C))
Then transpose the matrix:
(apply map vector *1)=> ([1 :a :A] [2 :b :B] [3 :c :C])
Finally zipmap with the provided keys for each row:
(mapv zipmap (repeat ks) *1)=> [{:index 1, :lower :a, :upper :A} {:index 2, :lower :b, :upper :B} {:index 3, :lower :c, :upper :C}]
If you can provide a list of the key-values, like the following (formatted to improve readability):
(def items [[{:index 1} {:index 2} {:index 3}]
[{:lower :a} {:lower :b} {:lower :c}]
[{:upper :A} {:upper :B} {:upper :C}]])
then you can use the following:
(apply map merge items)
;; returns ({:index 1, :lower :a, :upper :A} {:index 2, :lower :b, :upper :B} {:index 3, :lower :c, :upper :C})
This works by using the map function to merge the individual hash-maps in the 3 collections. First, the first elements of each collection are merged together, resulting in the element {:index 1, :lower :a, :upper :A}, then the second elements of each collection are merged, and so on.
Since the arguments to map merge are a collection, you need to use apply to provide the arguments to map.
I am not a clojure expert but maybe this helps:
; your data
(def x [1 2 3 :a :b :c :A :B :C])
; resolve symbols and numbers to strings
(def xa (map (fn [e] (if (keyword? e) (name e) (str e))) x))
; split into three sequences and zip this lists together
(let [xan (filter (fn [e] (not (empty? (re-matches #"[0-9]" e)))) xa)
xaa (filter (fn [e] (not (empty? (re-matches #"[a-z]" e)))) xa)
xaA (filter (fn [e] (not (empty? (re-matches #"[A-Z]" e)))) xa)]
(map-indexed (fn [i e] {:index e :lower (nth xaa i) :upper (nth xaA i)}) xan ))
You just build three sequences and iterate over any of them and use the index to access the corresponding elements from the other.

Clojure/dataset: group-by multiple columns hierarchically?

I would like to implement a function that can group-by for multiple columns hierarchically. I can illustrate my requirement by the following tentative implementation for two columns:
(defn group-by-two-columns-hierarchically
[col1 col2 table]
(let [data-by-col1 ($group-by col1 table)
data-further-by-col2 (into {} (for [[k v] data-by-col1] [k ($group-by col2 v)]))
]
data-further-by-col2
))
I'm seeking help how to generalize on arbitrary number of columns.
(I understand that Incanter supports group-by for multiple columns but it only provides a structure not hierarchy, a map of composite key of multiple columns to value of datasets.)
Thanks for your help!
Note: to make MichaƂ's solution work for incanter dataset, only a slight modification is needed, replacing "group-by" by "incanter.core/$group-by", illustrated by the following experiment:
(defn group-by*
"Similar to group-by, but takes a collection of functions and returns
a hierarchically grouped result."
[fs coll]
(if-let [f (first fs)]
(into {} (map (fn [[k vs]]
[k (group-by* (next fs) vs)])
(incanter.core/$group-by f coll)))
coll))
(def table (incanter.core/dataset ["x1" "x2" "x3"]
[[1 2 3]
[1 2 30]
[4 5 6]
[4 5 60]
[7 8 9]
]))
(group-by* [:x1 :x2] table)
=>
{{:x1 1} {{:x2 2}
| x1 | x2 | x3 |
|----+----+----|
| 1 | 2 | 3 |
| 1 | 2 | 30 |
},
{:x1 4} {{:x2 5}
| x1 | x2 | x3 |
|----+----+----|
| 4 | 5 | 6 |
| 4 | 5 | 60 |
},
{:x1 7} {{:x2 8}
| x1 | x2 | x3 |
|----+----+----|
| 7 | 8 | 9 |
}}
(defn group-by*
"Similar to group-by, but takes a collection of functions and returns
a hierarchically grouped result."
[fs coll]
(if-let [f (first fs)]
(into {} (map (fn [[k vs]]
[k (group-by* (next fs) vs)])
(group-by f coll)))
coll))
Example:
user> (group-by* [:foo :bar :quux]
[{:foo 1 :bar 1 :quux 1 :asdf 1}
{:foo 1 :bar 1 :quux 2 :asdf 2}
{:foo 1 :bar 2 :quux 1 :asdf 3}
{:foo 1 :bar 2 :quux 2 :asdf 4}
{:foo 2 :bar 1 :quux 1 :asdf 5}
{:foo 2 :bar 1 :quux 2 :asdf 6}
{:foo 2 :bar 2 :quux 1 :asdf 7}
{:foo 2 :bar 2 :quux 2 :asdf 8}
{:foo 1 :bar 1 :quux 1 :asdf 9}
{:foo 1 :bar 1 :quux 2 :asdf 10}
{:foo 1 :bar 2 :quux 1 :asdf 11}
{:foo 1 :bar 2 :quux 2 :asdf 12}
{:foo 2 :bar 1 :quux 1 :asdf 13}
{:foo 2 :bar 1 :quux 2 :asdf 14}
{:foo 2 :bar 2 :quux 1 :asdf 15}
{:foo 2 :bar 2 :quux 2 :asdf 16}])
{1 {1 {1 [{:asdf 1, :bar 1, :foo 1, :quux 1}
{:asdf 9, :bar 1, :foo 1, :quux 1}],
2 [{:asdf 2, :bar 1, :foo 1, :quux 2}
{:asdf 10, :bar 1, :foo 1, :quux 2}]},
2 {1 [{:asdf 3, :bar 2, :foo 1, :quux 1}
{:asdf 11, :bar 2, :foo 1, :quux 1}],
2 [{:asdf 4, :bar 2, :foo 1, :quux 2}
{:asdf 12, :bar 2, :foo 1, :quux 2}]}},
2 {1 {1 [{:asdf 5, :bar 1, :foo 2, :quux 1}
{:asdf 13, :bar 1, :foo 2, :quux 1}],
2 [{:asdf 6, :bar 1, :foo 2, :quux 2}
{:asdf 14, :bar 1, :foo 2, :quux 2}]},
2 {1 [{:asdf 7, :bar 2, :foo 2, :quux 1}
{:asdf 15, :bar 2, :foo 2, :quux 1}],
2 [{:asdf 8, :bar 2, :foo 2, :quux 2}
{:asdf 16, :bar 2, :foo 2, :quux 2}]}}}

Clojure map transformations (children to fathers in a tree)

Say I have the following map of nodes in a tree and their children:
(def a {0 [], 1 [], 2 [0, 1]})
Corresponding to a tree with node 2 at its root and two leaf-nodes 0 and 1 as node 2's children.
How do I transform it into a map of fathers, or, better yet, adorn it with the fathers. E.g. arrive at the following map of fathers:
{0 2, 1 2, 2 nil} ; each node only has one father at most
Or, better yet, at the following map which combines children and fathers:
{0 [[] 2], 1 [[] 2], 2 [[0,1] nil]}
First bit:
(def a {0 [], 1 [], 2 [0, 1]})
(defn parent-map [m]
(reduce
(fn [x [k v]]
(into x (zipmap v (repeat k)))) {} m))
(def parent (parent-map a))
parent
=> {1 2, 0 2}
(parent 1)
=> 2
(parent 2)
=> nil
So, no need to have 2 nil explicitly in the parent map.
Second bit:
(defn parent-child-map [m]
(let [parent (parent-map m)]
(reduce
(fn [x [k v]]
(assoc x k [(m k) (parent k)])) {} m)))
(parent-child-map a)
=> {2 [[0 1] nil], 1 [[] 2], 0 [[] 2]}
Something a tad more interesting:
(def b {0 [], 1 [], 2 [], 3 [], 4 [0 1 2], 5 [3], 6 [4 5]})
(parent-child-map b)
=>
{6 [[4 5] nil],
5 [[3] 6],
4 [[0 1 2] 6],
3 [[] 5],
2 [[] 4],
1 [[] 4],
0 [[] 4]}
(defn parents [m]
(let [plist (into {} (for [[k v] m vv v] [vv k]))]
(into {} (map (fn [[k v]] [k [v (plist k)]]) m))))
(parents a)
=> {0 [[] 2], 1 [[] 2], 2 [[0 1] nil]}

Clojure priority-map

I have the following,
(let [t (priority-map-by (comparator (fn [[f1 _] [f2 _]]
(< f1 f2)))
:b [8 [2 1]])]
(assoc t :c [8 [2 3]]))
for some reason the associated item :c is modified after it is added to the map what I get is,
{:c [8 [2 1]], :b [8 [2 1]]}
what I expect is,
{:c [8 [2 3]], :b [8 [2 1]]}
am I missing something obvious why is the items value is modified? or is this a bug?
I got what I wanted by not using a comparator and returning -1 0 1 depending on the cond,
(let [t (priority-map-by (fn [x y]
(if (= x y)
0
(let [[f1 _] x
[f2 _] y]
(if (< f1 f2) -1 1))))
:b [8 [2 1]])]
(assoc t :c [8 [2 3]]))
that gives me what I want,
{:b [8 [2 1]], :c [8 [2 3]]}
Looking at the implementation for priority-map-by, it appears to be based on sorted-map-by, which holds that keys that compare the same are the same. For example:
(sorted-map-by (fn [[a] [b]] (< a b)) [1 2] :foo [1 3] :bar)
=> {[1 2] :bar}
This also holds true for sorted-set-by:
(sorted-set-by (fn [[a] [b]] (< a b)) [1 2] [1 3] [3 4])
=> #{[1 2] [3 4]}
Whether that's intended behavior for priority-map-by, you'd have to ask the author. I think it's reasonable.