tutorial online example clojure

clojure - online - ¿Eliminar valores nulos de un mapa?



clojure windows (9)

Tengo un mapa de Clojure que puede contener valores que son nulos y estoy tratando de escribir una función para eliminarlos, sin mucho éxito (soy nuevo en esto).

P.ej:

(def record {:a 1 :b 2 :c nil}) (merge (for [[k v] record :when (not (nil? v))] {k v}))

Esto da como resultado una secuencia de mapas, que no es lo que esperaba de la fusión:

({:a 1} {:b 2})

Me gustaría tener:

{:a 1, :b 2}


Aquí hay uno que funciona en mapas anidados:

(defn remove-nils [m] (let [f (fn [[k v]] (when v [k v]))] (postwalk (fn [x] (if (map? x) (into {} (map f x)) x)) m)))


Aquí hay uno que funciona en mapas y vectores:

(defn compact [coll] (cond (vector? coll) (into [] (filter (complement nil?) coll)) (map? coll) (into {} (filter (comp not nil? second) coll))))


Aunque el enfoque de Jürgen (filtrar segundo registro) obtiene mi voto por Niftiest Clojure Trick, pensé que iba a tirar otra manera, esta vez usando select-keys :

user> (select-keys record (for [[k v] record :when (not (nil? v))] k)) {:b 2, :a 1}


Podrías aplastarlo en un mapa:

(into {} (remove (fn [[k v]] (nil? v)) {:a 1 :b 2 :c nil})) => {:a 1 :b 2}


Puedes usar reducir.

user> (reduce (fn [m [k v]] (if (nil? v) m (assoc m k v))) {} record) {:b 2, :a 1}

Si, por algún motivo, desea mantener el pedido (lo que generalmente no es importante en un mapa), puede usar dissoc .

user> (reduce (fn [m [k v]] (if (nil? v) (dissoc m k) m)) record record) {:a 1, :b 2}


Solución de Jürgen Hötzel refinada para solucionar el problema nulo / falso

(into {} (filter #(not (nil? (val %))) {:a true :b false :c nil}))

Una versión un poco más corta de la solución @thnetos.

(into {} (remove #(nil? (val %)) {:a true :b false :c nil}))


Su comprensión de la lista devuelve una LISTA de mapas, por lo que debe APLICAR esta lista a la función de combinación como argumentos opcionales:

user> (apply merge (for [[k v] record :when (not (nil? v))] {k v})) {:b 2, :a 1}

Solución más concisa al filtrar el mapa como una secuencia y unirlo en un mapa:

user> (into {} (filter second record)) {:a 1, :b 2}

No elimine los valores falsos :

user> (into {} (remove (comp nil? second) record)) {:a 1, :b false}

Usar dissoc para permitir el intercambio de datos persistente en lugar de crear un mapa completamente nuevo:

user> (apply dissoc record (for [[k v] record :when (nil? v)] k)) {:a 1, :b 2}


Una variación en la respuesta de @ Eelco:

(defn remove-nils [m] (let [f (fn [x] (if (map? x) (let [kvs (filter (comp not nil? second) x)] (if (empty? kvs) nil (into {} kvs))) x))] (clojure.walk/postwalk f m)))

Para el punto de @ broma0, elude cualquier mapa vacío.

user> (def m {:a nil, :b 1, :c {:z 4, :y 5, :x nil}, :d {:w nil, :v nil}}) user> (remove-nils m) {:b 1, :c {:z 4, :y 5}} user> (remove-nils {}) nil


reduce-kv también se puede utilizar para eliminar las claves

(reduce-kv (fn [m key value] (if (nil? value) (dissoc m key) m)) {:test nil, :test1 "hello"} {:test nil, :test1 "hello"})