Costruire una mappa e dissoc
ing le chiavi che si vuole imporre condizioni in base in base a un predicato (qui - nil?
) potrebbe essere l'approccio più semplice (NB questa funzione verifica solo le chiavi esplicitamente menzionate come argomenti, quelle non menzionate non vengono mai rimosse, se i valori ad esse associati soddisfano o meno il predicato):
(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}
Anche se in generale, se si prevede di lavorare con un sacco di mappe che rappresentano entità del mondo reale di un certo tipo particolare, si potrebbe desiderare di andare con i record - e quindi puoi semplicemente saltare tutti gli nil
s nella fase in cui estrai i valori dalla tua mappa di input, perché i record, se visualizzati come mappe, sembrano sempre includere le chiavi corrispondenti ai loro campi. Per esempio.
(defrecord Person [username first-name last-name])
Poi si può scomporre la logica per "conversioni schema" tra mappe:
(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 {})))
Ora la funzione create-record
diventa una composizione di translate-map
e 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]})))
Se preferisci lavorare con le mappe normali, potresti invece usare qualcosa del tipo seguente per un output 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]})))
Al REPL (versione record in 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}
a parte: "gender"! = "Sex" – tom