Search code examples
clojureclojure-core

Can avoiding nil conjoins be done better?


I have a token scanner that simply returns nil for characters I'm not interested in. Rather than conj the nils to my token vector and then later stripping them all out, I want to simply not add them.

I'm using

;; dont conjoin if value false
(defn condj [v val]
  (cond-> v, val (conj val)))

to do this. Is there a specific operator or a more concise implementation?


Solution

  • I like the cond-> version and often use that to avoid repetition in the if version. Don't forget to be explicit about false values, though. I also like to use Plumatic Schema to be explicit about the data shape entering and leaving the function:

    (ns tst.demo.core
      (:use tupelo.core tupelo.test)
      (:require
        [schema.core :as s]))
    
    (s/defn condj :- [s/Any]
      "Conjoin an item onto a vector of results if the item is not nil."
      [accum :- [s/Any]
       item :- s/Any]
      (cond-> accum
        (not (nil? item)) (conj item)))
    
    (dotest
      (let [result (-> []
                     (condj :a)
                     (condj :2)
                     (condj false)
                     (condj "four")
                     (condj nil)
                     (condj "last"))]
        ; nil is filtered but false is retained
        (is= result [:a :2 false "four" "last"])))
    

    You may also be interested in another version using my favorite library:

    (s/defn condj :- [s/Any]
      "Conjoin an item onto a vector of results if the item is not nil."
      [accum :- [s/Any]
       item :- s/Any]
      (cond-it-> accum
        (not-nil? item) (append it item)))
    

    For more complicated forms, using the placeholder symbol it makes it explicit where the value is being threaded. I also like the convenience functions not-nil? and append since they also make the intent of the code plainer.