(def v [:1 :1 :1 :2 :2 :2 :3 :3])
(defn groupFirstElem [[vec & more :as myList]]
(split-with (partial = vec) myList)
)
;the (groupFirstElem v) yields [(:1 :1 :1) (:2 :2 :2 :3 :3)]
the idea is to use groupFirstElem function to obtain this
[(:1 :1 :1) (:2 :2 :2) (:3 :3)]
how do i go about recursevely calling groupFirstElem so that it applies to each "group" of v without having to evaluate the first element twice.
First of all, your specific problem can be solved using:
(partition-by identity [:1 :1 :1 :2 :2 :2 :3 :3])
;; => ((:1 :1 :1) (:2 :2 :2) (:3 :3))
Back to your question; the following would repeatedly replace the last element of a sq with the (spliced) result of a function:
(defn iterate-last
[f sq]
(loop [sq sq]
(if (empty? (last sq))
(butlast sq)
(recur (concat (butlast sq) (f (last sq)))))))
A small caveat, you have to call it like this:
(iterate-last groupFirstElem [v])
;; => ((:1 :1 :1) (:2 :2 :2) (:3 :3))
Related
Suppose i want to change a number in a loop to a keyword in order to check an entry in a dict/map:
(def myarray {:p1 "test"})
#'user/myarray
user> (get myarray
(keyword ":p1")
)
nil
user> (get myarray
(symbol ":p1")
)
nil
user>
I am just getting nil returned. What do i miss here?
: is the indicator of keyword according to the Clojure guide, and the keyword function adds : automatically according to the Clojure Docs. So the correct code must be (keyword "p1") instead of (keyword ":p1").
Here is an outline of how you can do it:
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test))
(dotest
(let [m1 {:p1 "a"
:p2 "b"}
k1 (keyword (str "p" 1))
k2 (keyword (str "p" 2))
v1 (k1 m1)
v2 (k2 m1)]
(is= [k1 k2] [:p1 :p2])
(is= [v1 v2] ["a" "b"])))
Note though, an integer is a perfectly valid map key:
(dotest
(let [m1 {1 "a"
2 "b"}
k1 1
k2 2
v1 (get m1 k1) ; this doesn't work: (k1 m1 )
v2 (get m1 k2)]
(is= [k1 k2] [1 2])
(is= [v1 v2] ["a" "b"])))
You just have to use the get function (with the map as 1st arg!).
The above code is based on this template project.
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.
In Clojure, is there a way to make a var constant such that it can be used in case statements?
e.g.
(def a 1)
(def b 2)
(let [x 1]
(case x
a :1
b :2
:none))
=> :none
I understand I can use something like cond or condp to get around this, but it would be nice if I could define something that does not require further evaluation so I could use case.
Related and answer stolen from it:
As the docstring tells you: No you cannot do this. You can use Chas Emericks macro and do this however:
(defmacro case+
"Same as case, but evaluates dispatch values, needed for referring to
class and def'ed constants as well as java.util.Enum instances."
[value & clauses]
(let [clauses (partition 2 2 nil clauses)
default (when (-> clauses last count (== 1))
(last clauses))
clauses (if default (drop-last clauses) clauses)
eval-dispatch (fn [d]
(if (list? d)
(map eval d)
(eval d)))]
`(case ~value
~#(concat (->> clauses
(map #(-> % first eval-dispatch (list (second %))))
(mapcat identity))
default))))
Thus:
(def ^:const a 1)
(def ^:const b 2)
(let [x 1]
(case+ x
a :1
b :2
:none))
=> :1
An alternative (which is nice since it's more powerful) is to use core.match's functionality. Though you can only match against local bindings:
(let [x 2
a a
b b]
(match x
a :1
b :2
:none))
=> :2
You can also use clojure.core/condp for the job:
(def a 1)
(def b 2)
(let [x 1]
(condp = x
a :1
b :2
:none))
#=> :1
What is the idiomatic way of counting certain properties of a nested map of maps in Clojure?
Given the following datastructure:
(def x {
:0 {:attrs {:attributes {:dontcare "something"
:1 {:attrs {:abc "some value"}}}}}
:1 {:attrs {:attributes {:dontcare "something"
:1 {:attrs {:abc "some value"}}}}}
:9 {:attrs {:attributes {:dontcare "something"
:5 {:attrs {:xyz "some value"}}}}}})
How can i produce the desired output:
(= (count-attributes x) {:abc 2, :xyz 1})
This is my best effort so far:
(defn count-attributes
[input]
(let [result (for [[_ {{attributes :attributes} :attrs}] x
:let [v (into {} (remove (comp not :attrs) (vals attributes)))]]
(:attrs v))]
(frequencies result)))
Which produces the following:
{{:abc "some value"} 2, {:xyz "some value"} 1}
I like building such functions with threadding so the steps are easier to read
user> (->> x
vals ; first throw out the keys
(map #(get-in % [:attrs :attributes])) ; get the nested maps
(map vals) ; again throw out the keys
(map #(filter map? %)) ; throw out the "something" ones.
flatten ; we no longer need the sequence sequences
(map vals) ; and again we don't care about the keys
flatten ; the map put them back into a list of lists
frequencies) ; and then count them.
{{:abc "some value"} 2, {:xyz "some value"} 1}
(remove (comp not :attrs) is a lot like select-keys
for [[_ {{attributes :attributes} :attrs}] reminds me of get-in
I find tree-seq very useful for these cases:
(frequencies (filter #(and (map? %) (not-any? map? (vals %))) (tree-seq map? vals x)))
the motivation is for checking what has changed in a deeply nest map, kind of like a reverse of update-in.
This is a simple example:
(def p1 {:a {:a1 :1 :a2 :2}
:b {:b1 :1 :b2 :2}})
(def p2 (update-in p1 [:a :a1] (constantly :updated))
;; => {:a {:a1 :updated :a2 :2}
;; :b {:b1 :1 :b2 :2}}
(what-changed? p1 p2)
;; => {:keys [:a :a1] :value :updated)
(what-changed? p2 p1)
;; => {:keys [:a :a1] :value :2)
I'm hoping that because clojure maps are persistent data-structures, there may be a smart algorithm to figure this out by looking at the underlying structure as opposed to walking through the nested maps and comparing the difference.
I would imagine a straight-forward recursive algorithm like (if (= map1 map2) :no-change (recursively-check-children ...)) already takes good advantage of the structure sharing, unless you're dealing with very wide trees (lots of branches per node). In any case you probably want to check out clojure.data/diff which solves a general, non-recursive, version of your problem, and also starts out by doing a straight (= a b)
Persistent data structures is only about implementation and not about "looking at the underlying structure".
As Joost said (+1) you can use "diff". It only needs to convert the answer using your "{:keys ... :value ...}" pattern:
(def p1 {:a {:a1 :1 :a2 :2}
:b {:b1 :1 :b2 {:b11 :11 :b22 :22}}})
(def p2 {:a {:a1 :updated1 :a2 :2}
:b {:b1 :1 :b2 {:b11 :updated2 :b22 :updated3}}})
(defn what-changed?* [m]
(if (not (map? m))
[(list m)]
(apply concat (map (fn [k]
(map (fn [nest-k]
(conj nest-k k))
(nested-keys (m k))))
(keys m)))))
(defn what-changed? [m1 m2]
(map (fn [l] {:keys (drop-last l) :value (last l)})
(nested-keys (second (data/diff m1 m2)))))
Test:
(what-changed? p1 p2)
-> ({:keys (:a :a1), :value :updated1}
{:keys (:b :b2 :b11), :value :updated2}
{:keys (:b :b2 :b22), :value :updated3})
(what-changed? p2 p1)
-> ({:keys (:a :a1), :value :1}
{:keys (:b :b2 :b11), :value :11}
{:keys (:b :b2 :b22), :value :22})
BTW in your case you can modify hashmap by "assoc-in" instead of "update-in":
(assoc-in {:a {:a1 :1 :a2 :2}
:b {:b1 :1 :b2 :2}}
[:a :a1] :updated)
-> {:a {:a2 :2, :a1 :updated}
:b {:b2 :2, :b1 :1}}