【问题标题】: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))]))))))