Trim 使用幽灵的深层嵌套数据结构中的不必要条目

Trim unnecessary entries from deeply nested data structure using specter

我希望使用 Clojure Spectre 来简化深度嵌套的数据结构。我要删除:

像这样:

(do-something
    {:a {:aa 1}                                               
       :b {:ba -1                                               
           :bb 2                                                
           :bc nil
           :bd ""
           :be []
           :bf {}
           :bg {:ga nil}
           :bh [nil]
           :bi [{}]
           :bj [{:ja nil}]}
       :c nil
       :d ""
       :e []
       :f {}
       :g {:ga nil}
       :h [nil]
       :i [{}]
       :j [{:ja nil}]})
    =>
    {:a {:aa 1} 
         :b {:ba -1 
             :bb 2}}

我在香草 Clojure 中有一些东西:

(defn prunable?
  [v]
  (if (sequential? v)
    (keep identity v)
    (or (nil? v) (#{"" [] {}} v))))

(defn- remove-nil-values
  [ticket]
  (clojure.walk/postwalk
    (fn [el]
      (if (map? el)
        (let [m (into {} (remove (comp prunable? second) el))]
          (when (seq m)
            m))
        el))
    ticket))

我想我需要某种 recursive-path 但我进展不快。非常感谢帮助。

比较不同版本与 spectre 实现的性能:

@bm1729 原版:

Evaluation count : 1060560 in 60 samples of 17676 calls.
             Execution time mean : 57.083226 µs
    Execution time std-deviation : 543.184398 ns
   Execution time lower quantile : 56.559237 µs ( 2.5%)
   Execution time upper quantile : 58.519433 µs (97.5%)
                   Overhead used : 7.023993 ns

Found 5 outliers in 60 samples (8.3333 %)
    low-severe   3 (5.0000 %)
    low-mild     2 (3.3333 %)
 Variance from outliers : 1.6389 % Variance is slightly inflated by outliers

以下版本:

Evaluation count : 3621960 in 60 samples of 60366 calls.
             Execution time mean : 16.606135 µs
    Execution time std-deviation : 141.114975 ns
   Execution time lower quantile : 16.481250 µs ( 2.5%)
   Execution time upper quantile : 16.922734 µs (97.5%)
                   Overhead used : 7.023993 ns

Found 9 outliers in 60 samples (15.0000 %)
    low-severe   6 (10.0000 %)
    low-mild     3 (5.0000 %)
 Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
(defn prune [x]
  (cond
    (map? x) (not-empty
              (reduce-kv
               (fn [s k v]
                 (let [v' (prune v)]
                   (cond-> s
                     v' (assoc k v'))))
               (empty x)
               x))

    (seqable? x) (not-empty
                  (into
                   (empty x)
                   (->> x (map prune) (filter identity))))

    :else x))

测试用例:

(prune {:a {:aa 1}
        :b  {:ba -1
             :bb 2
             :bc nil
             :bd ""
             :be []
             :bf {}
             :bg {:ga nil}
             :bh [nil]
             :bi [{}]
             :bj [{:ja nil}]}
        :c  nil
        :d  ""
        :e  []
        :f  {}
        :g  {:ga nil}
        :h  [nil]
        :i  [{}]
        :j  [{:ja nil}]})
;; => {:b {:bb 2, :ba -1}, :a {:aa 1}}

更新 - @bm1729 幽灵版本

Evaluation count : 3314820 in 60 samples of 55247 calls.
             Execution time mean : 18.421613 µs
    Execution time std-deviation : 591.106243 ns
   Execution time lower quantile : 18.148204 µs ( 2.5%)
   Execution time upper quantile : 20.674292 µs (97.5%)
                   Overhead used : 7.065044 ns

Found 8 outliers in 60 samples (13.3333 %)
    low-severe   2 (3.3333 %)
    low-mild     6 (10.0000 %)
 Variance from outliers : 18.9883 % Variance is moderately inflated by outliers

感谢 Clojurians slack 频道上的 nathanmarz

(def COMPACTED-VALS-PATH
  (recursive-path [] p
                  (continue-then-stay
                    (cond-path
                      map? [(compact MAP-VALS) p]
                      vector? [(compact ALL) p]))))

(defn- compact-data
  [m]
  (setval [MAP-VALS COMPACTED-VALS-PATH #(or (nil? %) (= "" %))] NONE m))