How to return a clojure card with fixed keys and conditional values?

I have a function that returns a map. The keys are static, but the values ​​are conditional. Like this:

(defn map-returning-function [xy] {:a (if (some-test-fn x) "one value" "other value" :b (if (some-test-fn x) 15 25 :c (if (different-test y) :one :two}) 

Is there an even more elegant way to achieve this without having to write an if test for each value? The only other way I can think of is

 (defn another-map-returning-function [xy] (if (some-test-fn x) {:a "one value", :b 15, :c (if (different-test y) :one :two)} {:a "other value" :b 25, :c (if (different-test y) :one :two)})) 

which doesn’t seem much better to me, because it repeats the key names for each branch of the conditional expression and repeats the function call on different-test . And God forbid, I need cond instead of if .

+6
source share
6 answers

How about this:

 (let [test-x (some-test x) test-y (some-test y)] (conj (if test-x {:a "one value" :b 15} {:a "other value" :b 25}) (if test-y {:c :one} {:c :two}))) 

Conditions are met once and in one place, and then used in another. However, it depends on the context and personal preferences. Something like your example might be cleaner:

 (let [test-x (some-test x) test-y (some-test y)] {:a (if test-x "one value" "other value") :b (if test-x 15 25) :c (if test-y :one :two)}) 
+4
source

Another option you might think of is to use merge . One case is used by default, which varies depending on the arguments of the function. This way you can easily group tests and season some comments if necessary.

 (defn map-returning-function [xy] (merge {:a "other value" :b 25 :c :two} (when (some-test-fn x) {:a "one value" :b 15}) (when (different-test y) {:c :one}))) 

Alternatively, vice versa, depending on what you think is the default.

 (defn map-returning-function [xy] (merge {:a "one value" :b 15 :c :one} (when-not (some-test-fn x) {:a "other value" :b 25}) (when-not (different-test y) {:c :two}))) 
+4
source

Looking at what you are asking, I would say that one way to do this is to build a function that looks something like this:

 (pred-map {:a [some-test-fn "one-value" "other-value"] :b [some-test-fn 15 25] :c [different-test :one :two]} xy) 

where x is the argument for all references of the first exchange function and y to the second

a way to achieve this may be as follows:

 (defn get-value [p tv fv a] (if (pa) tv fv)) (defn get-predicate-set [m] (set (map first (vals m)))) (defn get-arg-map [m args] (zipmap (get-predicate-set m) args)) (defn get-arg [pm args] ((get-arg-map m args) p)) (defn get-key-value-pair-creator [m args] (fn [[k [p tv fv]]] [k (get-value p tv fv (get-arg pm args))])) (defn pred-map [m & args] (into {} (map (get-key-value-pair-creator m args) m))) 

However, these functions rely on arguments that map functions to the principle of equality (which seems to go with references), so it will not understand two equal anonymous functions as one and the same.

if you don't mind repeating the arguments you create, a simpler function is as follows:

 (pred-map {:a [(some-test-fn x) "one value" "other-value"] :b [(some-test-fn x) 15 25] :c [(different-test y) :one :two]}) 

following a simple function:

 (defn pred-map [m] (into {} (for [[k [p tv fv]] m] [k (if p tv fv)]))) 

or in an inconsolable style:

 (def pred-map (comp (partial into {}) (partial map (fn [[k [p tv fv]]] [k (if p tv fv)])))) 
+2
source

Another way to do this :-)

 (defmulti map-returning-function (fn [xy] [ (some-test-fn x) (different-test y) ])) (let [x-values {true {:a "one value" :b 15} false {:a "other value" :b 25}} y-values {true {:c :one} false {:c :two}}] (defmethod map-returning-function [false false] [xy] (merge (x-values false) (y-values false))) (defmethod map-returning-function [true true] [xy] (merge (x-values true) (y-values true))) ...) 
+1
source

Your first code example seems to me the most readable. Readability is often preferable to efficiency if you do not have an important piece of code to work with your program. But here you can only evaluate your conventions once. I doubt very much that its performance matters a lot with your code. As for elegance, I still prefer your first example, as it’s clearer to see what exactly are key-value pairs.

 (defn another-map-returning-function [xy] (let [first-map (if (some test-fn x) {:a "one value" :b 15} {:a "other value" :b 25})] (assoc first-map :c (if (different-test y) :one :two)))) 
0
source

Shamelessly stealing other ideas.

 (defn map-returning-function [xy] (let [x-values {true {:a "one value" :b 15} false {:a "other value" :b 25}} y-values {true {:c :one} false {:c :two}} x-key (some-test-fn x) y-key (different-test y) ] (merge (x-values x-key) (y-values y-key)))) 
0
source

Source: https://habr.com/ru/post/890020/


All Articles