问题
I have (for instance) a mix of data structures such as {:name "Peter" :children "Mark"}
and {:name "Mark" :children ["Julia" "John"]
i.e. :children value is either a single string or a collection of strings. Other functions in my code expect that the value of :children is always a collection of strings, so I need to adapt the data for them.
Of course I can use something like:
(defn data-adapter [m]
(let [children (:children m)]
(assoc m :children
(if (coll? children)
children
[children]))))
But is there a more idiomatic/laconic way?
回答1:
I think you will have to take no for an answer.
(if (coll? x) x [x])
is about as terse and expressive as it gets. It’s what people usually use for this problem (sometimes with sequential?
instead of coll?
).
cond->
enthusiasts like me sometimes try to use it in place of a simple conditional, but here it is no improvement:
(cond-> x (not (coll? x)) vector)
In the context of your code, however, you can do a little better. A lookup and association is best expressed with update
:
(defn data-adapter [m]
(update m :children #(if (coll? %) % [%])))
回答2:
the only advice would be to abstract that logic to some function, to keep your actual business logic clean.
(defn data-adapter [m]
(let [children (:children m)]
(assoc m :children (ensure-coll children))))
or, more concise, with update
:
(defn data-adapter [m]
(update m :children ensure-coll))
where ensure-coll
could be something like this:
(defn iffun [check & {:keys [t f] :or {t identity f identity}}]
#((if (check %) t f) %))
(def ensure-coll (iffun coll? :f list))
(or whatever another implementation you like)
user> (data-adapter {:children 1})
;;=> {:children (1)}
user> (data-adapter {:children [1]})
;;=> {:children [1]}
来源:https://stackoverflow.com/questions/59453099/idiomatic-way-to-wrap-object-into-collection-if-its-not-a-collection-already