Can I refer to a clojure hashmap value from another value in the same map? - clojure

I'm trying to come up with some way for the values in a clojure hashmap to refer to each other. Conceptually, something like this:
(def m {:a 1 :b 5 :c (+ (:a m) (:b m))} ;Implies (= (:c m) 6)
This doesn't work, of course since I'm circularly referencing m. I can do something like
(def m {:a 1 :b 5 :c (fn [a b] (+ a b))})
((:c m) (:a m) (:b m)) ;=> 6
but that doesn't really gain anything because I still have to know which a and b to put into the function. Another attempt:
(def m {:a 1 :b 5 :c (fn [m] (+ (:a m) (:b m)))})
((:c m) m) ;=> 6
It's a bit better since I've now internalized the function to a map though not specifically this map. I might try to fix that with something like this
(defn new-get [k m]
(let [v-or-fn (get m k)]
(if (fn? v-or-fn) (v-or-fn m) v-or-fn)))
(def m {:a 1 :b 5 :c (fn [m] (+ (:a m) (:b m)))})
(new-get :a m) ;=> 1
(new-get :b m) ;=> 5
(new-get :c m) ;=> 6
I think this is about the best I can do. Am I missing something more clever?

Couldn't help myself from writing a macro:
(defmacro defmap [name m]
(let [mm (into [] (map (fn [[k v]] `[~k (fn [~name] ~v)]) m))]
`(def ~name
(loop [result# {} mp# (seq ~mm)]
(if (seq mp#)
(let [[k# v#] (first mp#)]
(recur (assoc result# k# (v# result#)) (rest mp#)))
result#)))))
(defmap m [[:a 1]
[:b 5]
[:c (+ (:a m) (:b m))]])
;; m is {:a 1 :b 5 :c 6}

As I've already said in comment above you can use let form:
(def m
(let [a 1 b 5]
{:a a :b b :c (+ a b)}))
This should be fine if you're using values that known only inside m definition. Otherwise you would better to use function parameters as #Michiel shown.
P.S. by the way you're free to use everything inside def you're usually use in clojure. Moreover, sometimes you're free to use let in sugared form inside some other forms (although this let uses different mechanisms than usual let form):
(for [x (...) xs]
:let [y (+ x 1)]
; ...

Since c is a derived value, so a function, of a and b you're probably better of by defining a function that produces this map:
(defn my-map-fn [a b]
{:a a :b b :c (+ a b)})
(def my-map (my-map-fn 1 2))
(:c my-map) ;;=> 3

Here is my take on it:
(defmacro let-map [& bindings]
(let [symbol-keys (->> bindings (partition 2) (map first))]
`(let [~#bindings]
(into {} ~(mapv (fn [k] [(keyword k) k]) symbol-keys)))))
;; if you view it as similar to let, when it's more complicated:
(let-map
a 1
b 5
c (+ a b)) ; => {:a 1, :b 5, :c 6}
;; if you see it as an augmented hash-map, when it's simple enough:
(let-map a 1, b 5, c (+ a b)) ; => {:a 1, :b 5, :c 6}

Related

compare Clojure maps with bigdecimal and integers values

In my REPL :
(== 1 1M)
;;=>true
(= {:a 1 :b 2} {:a 1 :b 2})
;;=>true
(= {:a 1 :b 2} {:a 1 :b 3})
;;=>false
(= {:a 1M :b 2M} {:a 1 :b 2})
;;=>false
How can I compare these maps so that the result of the last example would return true ?
You want to make sure that:
1) The maps have the same keys.
2) every value for a key has an equivalent (==) value in both maps.
Here's my first thought, I'm sure it could be made more succint:
(defn number-equivalent
[m1 m2]
(let [k1 (keys m1)]
(and (= k1 (keys m2))
(every? true?
(for [k k1]
(== (m1 k)
(m2 k)))))))
Here's a solution that will work for multiple maps
(defn number-equivalent [& ms]
(->> (apply merge-with == ms)
(every? (comp true? val))))
(defn map== [a b]
(and (= (count a) (count b))
(reduce-kv (fn [_ k va]
(or (and (number? va)
(let [vb (get b k)]
(and (number? vb)
(== va vb))))
(reduced false)))
true a)))
Compare corresponding values with ==
(defn compare-nums
[m1 m2]
(every? (fn [[k v]] (== (get m1 k) v)) m2))
Note that both maps need to have the same keys. You might want to add a precondition like
{:pre [(= (.keySet m1) (.keySet m2))]} ; ensure both maps have the same keys

how can you interleave two vectors of differing lengths in clojure

What is the simplest way to interleave two vectors with n+1 and n members?
(def a [:a :c :e])
(def b [:b :d])
(interleave a b ); truncates to shortest list
[:a :b :c :d]
;what I would like.
(interleave-until-nil a b)
[:a :b :c :d :e]
Cons the first, interleave the rest with arguments reversed.
(cons (first a) (interleave b (rest a)))
;=> (:a :b :c :d :e)
Conj nil to the second, interleave colls get all butlast
(butlast (interleave a (conj b nil)))
;=> (:a :b :c :d :e)
(defn interleave+ [& x]
(take (* (count x) (apply max (map count x)))
(apply interleave (map cycle x))))
(butlast (interleave+ [:a :c :e] [:b :d]))
=> (:a :b :c :d :e)
Tried this as an exercise in lazy seqs. I suspect that there are more elegant ways though.
(defn interleave-all
"interleaves including remainder of longer seqs."
[& seqs]
(if (not-empty (first seqs))
(cons (first (first seqs)) (lazy-seq (apply interleave-all (filter not-empty (concat (rest seqs) [(rest (first seqs))])))))))
If you would like to have nil appended to always have same dimension results, this could be a way to do that:
(defn interleave-all [& seqs]
(reduce
(fn [a i]
(into a (map #(get % i) seqs)))
[]
(range (apply max (map count seqs)))))
For example:
(interleave-all [:a] [:b :c])
outputs:
[:a :b nil :c]
This can be used to transpose a matrix:
(defn matrix-transpose [input]
(partition
(count input)
(apply interleave-all input)))
Example:
(matrix-transpose [[:a] [:b :c]])
Outputs:
[[:a :b] [nil :c]]
Which can be used to i.e. tabular output of lists of differing lengths (but where you need the fixed dimensions to insert nothing where lists have no value for certain indices).

How to reduce a nested collection without using mutable state?

Given a nested collection I would like to reduce it to only the k-v pairs which are the form [_ D] where D is an integer. For instance I would like to transform as follows:
; Start with this ...
{:a {:val 1 :val 2} :b {:val 3 :c {:val 4}} :val 5}
; ... end with this
{:val 1, :val 2, :val 3, :val 4, :val 5}
I have written a function using postwalk as follows:
(defn mindwave-values [data]
(let [values (atom {})
integer-walk (fn [x]
(if (map? x)
(doseq [[k v] x]
(if (integer? v) (swap! values assoc k v)))
x))]
(postwalk integer-walk data)
#values))
I am curious if it is possible to do this without using mutable state?
EDIT The original function was not quite correct.
Your example data structure is not a legal map, so I've changed it a bit:
(defn int-vals [x]
(cond (map? x) (mapcat int-vals x)
(coll? x) (when (= 2 (count x))
(if (integer? (second x))
[x]
(int-vals (second x))))))
user> (int-vals {:a {:x 1 :y 2} :b {:val 3 :c {:val 4}} :val 5})
([:y 2] [:x 1] [:val 4] [:val 3] [:val 5])
Your requirements are a bit vague: you say "collection", but your example contains only maps, so I've just had to guess at what you intended.

How to Increment Values in a Map

I am wrapping my head around state in Clojure. I come from languages where state can be mutated. For example, in Python, I can create a dictionary, put some string => integer pairs inside, and then walk over the dictionary and increment the values.
How would I do this in idiomatic Clojure?
(def my-map {:a 1 :b 2})
(zipmap (keys my-map) (map inc (vals my-map)))
;;=> {:b 3, :a 2}
To update only one value by key:
(update-in my-map [:b] inc) ;;=> {:a 1, :b 3}
Since Clojure 1.7 it's also possible to use update:
(update my-map :b inc)
Just produce a new map and use it:
(def m {:a 3 :b 4})
(apply merge
(map (fn [[k v]] {k (inc v) }) m))
; {:b 5, :a 4}
To update multiple values, you could also take advantage of reduce taking an already filled accumulator, and applying a function on that and every member of the following collection.
=> (reduce (fn [a k] (update-in a k inc)) {:a 1 :b 2 :c 3 :d 4} [[:a] [:c]])
{:a 2, :c 4, :b 2, :d 4}
Be aware of the keys needing to be enclosed in vectors, but you can still do multiple update-ins in nested structures like the original update in.
If you made it a generalized function, you could automatically wrap a vector over a key by testing it with coll?:
(defn multi-update-in
[m v f & args]
(reduce
(fn [acc p] (apply
(partial update-in acc (if (coll? p) p (vector p)) f)
args)) m v))
which would allow for single-level/key updates without the need for wrapping the keys in vectors
=> (multi-update-in {:a 1 :b 2 :c 3 :d 4} [:a :c] inc)
{:a 2, :c 4, :b 2, :d 4}
but still be able to do nested updates
(def people
{"keith" {:age 27 :hobby "needlefelting"}
"penelope" {:age 39 :hobby "thaiboxing"}
"brian" {:age 12 :hobby "rocket science"}})
=> (multi-update-in people [["keith" :age] ["brian" :age]] inc)
{"keith" {:age 28, :hobby "needlefelting"},
"penelope" {:age 39, :hobby "thaiboxing"},
"brian" {:age 13, :hobby "rocket science"}}
To slightly improve #Michiel Brokent's answer. This will work if the key already doesn't present.
(update my-map :a #(if (nil? %) 1 (inc %)))
I've been toying with the same idea, so I came up with:
(defn remap
"returns a function which takes a map as argument
and applies f to each value in the map"
[f]
#(into {} (map (fn [[k v]] [k (f v)]) %)))
((remap inc) {:foo 1})
;=> {:foo 2}
or
(def inc-vals (remap inc))
(inc-vals {:foo 1})
;=> {:foo 2}

How do I conj to a clojure vector conditionally

Is there a cleaner way to do something like the following in clojure?
(defn this [x] (* 2 x))
(defn that [x] (inc x))
(defn the-other [x] (-> x this that))
(defn make-vector [thing]
(let [base (vector (this (:a thing))
(that (:b thing)))]
(if-let [optional (:c thing)]
(conj base (the-other optional))
base)))
(make-vector {:a 1, :b 2}) ;=> [2 3]
(make-vector {:a 1, :b 2, :c 3}) ;=> [2 3 7]
By "cleaner" I mean something closer to this:
(defn non-working-make-vector [thing]
(vector (this (:a thing))
(that (:b thing))
(if (:c thing) (the-other (:c thing)))))
(non-working-make-vector {:a 1, :b 2} ;=> [2 3 nil] no nil, please!
(non-working-make-vector {:a 1, :b 2, :c 3} ;=> [2 3 7]
Note that I might want to call some arbitrary function (e.g. this, that, the-other) on any of the keys in thing and place the result in the returned vector. The important thing is that if the key doesn't exist in the map it should not put a nil in the vector.
This is similar to this question but the output is a vector rather than a map so I can't use merge.
(defn this [x] (* 2 x))
(defn that [x] (inc x))
(defn the-other [x] (-> x this that))
(def k-f-map {:a this
:b that
:c the-other})
(def m1 {:a 1 :b 2})
(def m2 {:a 1 :b 2 :c 3})
(defn make-vector [k-f-map m]
(reduce (fn [res [fk fv]]
(if (fk m)
(conj res (fv (fk m)))
res))
[] k-f-map))
(make-vector k-f-map m1)
-> [2 3]
(make-vector k-f-map m2)
-> [2 3 7]
;;; replace [:a :b :c] with a vector of arbitrary functions
;;; of your choice, or perhaps accept a seqable of functions
;;; as an extra argument
(defn make-vector [thing]
(into [] (keep #(% thing) [:a :b :c])))
;;; from the REPL:
(make-vector {:a 1 :b 2})
; => [1 2]
(make-vector {:a 1 :b 2 :c 3})
; => [1 2 3]
Note that keep only throws out nil; false will be included in the output.
or using cond->?
your make-vector function in cond-> version:
(defn make-vector [thing]
(cond-> [(this (:a thing))
(that (:b thing))]
(:c thing) (conj (the-other (:c thing)))))
you can have more conditions or change :a and :b to be optional as well.