Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 856
timbo Avatar asked Oct 28 '25 05:10

timbo


2 Answers

I believe you can use transducers for this. They are explained here. Our reducing function is conj and we construct a transducer (remove nil?) that turns this function into one that will ignore nil:

(def condj ((remove nil?) conj))

Note that remove is the opposite of filter. We can also implement condj using (filter some?), some? being a function that is true for any value except nil:

(def condj ((filter some?) conj))

It seems to work:

user=> (condj [3 4 5] 9)
[3 4 5 9]
user=> (condj [3 4 5] nil)
[3 4 5]
user=> (condj [3 4 5] false)
[3 4 5 false]
like image 162
Rulle Avatar answered Oct 29 '25 20:10

Rulle


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.

like image 42
Alan Thompson Avatar answered Oct 29 '25 19:10

Alan Thompson



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!