stand
stand

Reputation: 3132

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.

Upvotes: 5

Views: 4148

Answers (3)

Henry Liu
Henry Liu

Reputation: 701

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.

Upvotes: 0

mobyte
mobyte

Reputation: 3752

(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]

Upvotes: 2

Michał Marczyk
Michał Marczyk

Reputation: 84331

;;; 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.

Upvotes: 1

Related Questions