La construcción de un mapa y dissoc
ing las teclas que desea imponer condiciones sobre la base de un predicado (aquí - nil?
) podría ser el enfoque más simple (Nota: esta función solo prueba las claves explícitamente mencionadas como argumentos, las que no se mencionan nunca se eliminan, si los valores adjuntos satisfacen el predicado o no):
(defn dissoc-when
"Dissoc those keys from m which are mentioned among ks and whose
values in m satisfy pred."
[pred m & ks]
(apply dissoc m (filter #(pred (m %)) ks)))
Al REPL:
user> (dissoc-when nil? {:foo nil :bar true :quux nil} :foo :bar)
{:quux nil, :bar true}
Aunque en términos generales, si va a trabajar con una gran cantidad de mapas que representan entidades del mundo real de algún tipo particular, es posible que quiera ir con registros - y a continuación, puede omitir todos los nil
s en la etapa donde extrae los valores de su mapa de entrada, porque los registros, cuando se ven como mapas, siempre parecen incluir las claves correspondientes a sus campos. P.ej.
(defrecord Person [username first-name last-name])
A continuación, puede factorizar la lógica de "conversiones de esquema" entre mapas:
(defn translate-map
"Transforms the data map in accordance with the spec in table.
Skips nil-valued entries."
[data table]
(->> table
(keep (fn [[to from]]
(when-let [from-val (get-in data from)]
[to from-val])))
(into {})))
Ahora su función create-record
se convierte en una composición de translate-map
y map->Person
:
(defn create-person [data]
(map->Person
(translate-map data {:username [:username]
:first-name [:user-info :name :first]
:last-name [:user-info :name :last]
:gender [:user-info :sex]})))
Si Si prefiere trabajar con mapas regulares, puede usar algo como lo siguiente en lugar de una salida equivalente:
(defn create-person [data]
(merge (zipmap [:username :first-name :last-name] (repeat nil))
(translate-map data {:username [:username]
:first-name [:user-info :name :first]
:last-name [:user-info :name :last]
:gender [:user-info :sex]})))
En el REPL (versión de registro en Clojure 1.3):
user> (create-person {:username "jsmith"
:user-info {:name {:first "John" :last "Smith"}}})
#user.Person{:username "jsmith", :first-name "John", :last-name "Smith"}
user> (create-person {:username "jsmith"
:user-info {:name {:first "John" :last "Smith"}
:sex :male}})
#user.Person{:username "jsmith", :first-name "John", :last-name "Smith", :gender :male}
aside: "gender"! = "Sex" – tom