我经常想用所有键构建地图,但是不希望键的值显示为零时显示它们。所以:
From: "My Company" <myemail@mydomain.com>
Subject: Your subject
To: xxxx@hotmail.com.br
Bcc: myotheremail@mydomain.com
Content-Type: text/plain; charset=us-ascii
MIME-Version: 1.0
Content-Transfer-Encoding: base64
Sender: My Company <myemail@mydomain.com>
Organization: My Organization
Date: Mon, 18 Nov 2019 09:19:05 -0300
VGhpcyBpcyBhIHRlc3Qgw6kgw6Egw7Mgw6cNCg==
.
将产生{:a 1:c 3:e 5}。 (显然,我事先不知道这些值,但出于演示目的对其进行硬编码很方便。)我决定构建自己的忽略nil的assoc型函数,称为“ asif”:
(building-map :a 1 :b nil :c 3 :d nil :e 5)
(我从查看 assoc 开始,所以我知道这就是所谓的“幼稚”实现。但是现在就运行它。)当然,因为我很贪婪,我希望它可以递归工作:
(defn asif [m & kvs]
(if (empty? kvs)
m
(if (second kvs)
(recur (assoc m (first kvs) (second kvs)) (drop 2 kvs))
(recur m (drop 2 kvs)))))
这应该返回{:a 1:b 2:c 3:e {:sub1 1:a 1}}。但这当然不会,因为它只是将“ e”上的地图视为单个值。
目前,我的解决方案是:
(asif {} :a 1 :b 2 :c 3 :d nil :e {:sub1 1 :sub2 nil :a 1})
这行得通,而且很好,但是这种情况“我已经用&收集了parm”,现在想对它们进行递归操作,但是我做不到,这总是让我感到困惑。我知道我可以做到:
但是反正可以使用&来做到这一点吗? (在其他方法中,我很讨厌“应用”。)或者是当您使用&收集了Parms时,模头已铸好了?
更新:只是为了澄清,这并不是删除nil,而是使用rest参数(因此是主题标题)。这只是我定期遇到的一个问题,并且(在此特定情况下)我今天想到“我可以用[&rest]编写一个递归忽略nil值的assoc模拟物”时碰巧又遇到了这个问题吗?
答案 0 :(得分:2)
我认为问题在于,您在顶级调用中所做的操作与递归调用中所做的不匹配。在顶级调用中,您具有一个映射和多个键/值对,但是在递归调用中,您仅具有一个映射,因此您不再真正assoc
进行操作,而是从现有映射中删除nil
所以我要说的是,由于您要做的是两种不同的事情,因此需要两种不同的功能。
这是一种从嵌套地图递归删除nil的简单方法:
(defn recursively-prune-nils [m]
(into {} (map (fn [[k v :as pair]]
[k
(if (map? v)
(recursively-prune-nils v)
v)])
(filter second m))))
我们在这里不能使用recur
,因为它不会处于尾部位置,因此,如果地图嵌套得非常深,最终将导致爆炸。
然后可以在asif
函数中使用它:
(defn asif [m & kvs]
(if (empty? kvs)
m
(if (second kvs)
(if (map? (second kvs))
(recur (assoc m (first kvs) (recursively-prune-nils (second kvs))) (drop 2 kvs))
(recur (assoc m (first kvs) (second kvs)) (drop 2 kvs)))
(recur m (drop 2 kvs)))))
这似乎可以满足您的要求:
user> (asif {} :a 1 :b 2 :c 3 :d nil :e {:sub1 1 :sub2 nil :a 1})
{:a 1, :b 2, :c 3, :e {:sub1 1, :a 1}}
答案 1 :(得分:0)
这就是我要怎么做(作为单元测试编写)。基本上,除非确实需要,否则不要重新发明轮子。只需使用postwalk
。
The code:
(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require
[clojure.walk :as walk]
[schema.core :as s]
[tupelo.schema :as tsk]
))
(def kv-data [:a 1 :b nil :c 3 :d nil :e {:x 1 :y nil :z 1}])
(s/defn map-remove-nil-vals :- tsk/Map
[map-in :- tsk/Map]
(reduce-kv
(fn [accum k v]
(if (nil? v)
accum
(assoc accum k v)))
{}
map-in))
(defn walk-drop-mapentry-nil-vals
[arg]
(walk/postwalk (fn [item]
(if (map? item)
(map-remove-nil-vals item)
item))
arg))
(dotest
(let [map-data (apply hash-map kv-data)
result (walk-drop-mapentry-nil-vals map-data)]
(is= (spyx-pretty result)
{:e {:x 1, :z 1}, :c 3, :a 1}
)))
和结果:
Testing _bootstrap
-------------------------------
Clojure 1.10.1 Java 13
-------------------------------
Testing tst.demo.core
result => {:e {:x 1, :z 1}, :c 3, :a 1}
Ran 2 tests containing 1 assertions.
0 failures, 0 errors.
Passed all tests