【问题标题】:Clojure - filter nested map on innermost levelClojure - 在最内层过滤嵌套地图
【发布时间】:2019-01-11 09:32:26
【问题描述】:

过滤以下嵌套地图的最佳方法是什么,保持嵌套地图结构。在我的示例中,可以复制 Alice 和 Bob,例如同一名员工可以同时在多个不同的工厂工作。

(def universe
  {:customer1
    {:plant1
      { "Alice" {:age 35 :sex "F"}
        "Bob" {:age 25 :sex "M"}}
     :plant2 {}} 
   :customer2 {}
  })

例如,我想按年龄 >30 过滤并返回相同的地图结构。理想情况下,这适用于任何嵌套的地图深度,在最内层进行过滤。预期结果:

(def universe
  {:customer1
    {:plant1
      { "Alice" {:age 35 :sex "F"}
        }
     :plant2 {}} 
   :customer2 {}
  })

我查看了clojure filter nested map to return keys based on inner map values,但它看起来并不能解决我的问题。谢谢,

【问题讨论】:

  • 如果您也显示所需的输出,那就太好了。

标签: dictionary filter clojure


【解决方案1】:

这与之前的问题之一非常相似:

(use '[com.rpl.specter])
(let [input          {:customer1
                                 {:plant1
                                          {"Alice" {:age 35 :sex "F"}
                                           "Bob"   {:age 25 :sex "M"}}
                                  :plant2 {}}
                      :customer2 {}}
      desired-output {:customer1
                                 {:plant1 {"Alice" {:age 35 :sex "F"}}
                                  :plant2 {}}
                      :customer2 {}}
      RECUR-MAP      (recursive-path [] p (cond-path map? (continue-then-stay [MAP-VALS p])))]

    (clojure.test/is (= (setval [RECUR-MAP (pred :age) #(> 30 (:age %))] NONE input)
                        desired-output)))

【讨论】:

    【解决方案2】:

    您的数据有点不寻常,因为人们通常会认为 :customer1:customer2 等是向量中的不同条目。对于这样的半结构化数据,我会考虑postwalk

    (ns tst.demo.core
      (:use tupelo.core demo.core tupelo.test)
      (:require
        [clojure.walk :as walk] ))
    
    (def universe
      {:customer1
        {:plant1
          {"Alice" {:age 35 :sex "F"}
           "Bob"   {:age 25 :sex "M"}}
          :plant2 {}}
       :customer2 {}})
    
    (def age-of-wisdom 30)
    
    (defn wisdom-only
      [form]
      (let [filter-entry? (when (map-entry? form)
                            (let [[-name- details]   form
                                  age                (:age details)] ; => nil if missing
                              (and age ; ensure not nil
                                (< age age-of-wisdom))))]
        (if filter-entry?
          {}
          form)))
    
    (walk/postwalk wisdom-only universe) => 
      {:customer1 
        {:plant1 
          {"Alice" {:age 35, :sex "F"}}
         :plant2 {}}
       :customer2 {}}
    

    【讨论】:

      【解决方案3】:

      感谢@akond 的回答,阅读代码让我想到了一个非幽灵解决方案。尽管如此,在这个用例中应用 filter 并不容易,这让我有点惊讶。

      (defn recursive-filter [pred k m]
          (letfn [(pair-filter [pair] (if (pred (k (second pair))) pair nil))]
          (into {}
              (for [a m]
                  (if (empty? (second a))
                      [(first a) {}]
                      (if (contains? (second a) k)
                          (pair-filter a)
                          [(first a) (recursive-filter pred k (second a))]))))))
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-11-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-19
        • 2015-10-16
        相关资源
        最近更新 更多