如何在Clojure中遍历复杂的映射并基于模式删除键



我对Clojure还很陌生,如果这是一个愚蠢的问题,我很抱歉,但我有一个相当复杂的映射(它包含嵌套的映射、向量等(。在我把它放进数据库之前,我基本上想分解一些我不需要的东西来节省写速度(在这种情况下,这是一种显著的性能改进(。我想通过制作某种模式来过滤掉所有不需要的部分,这种模式可以用来删除任何不必要的东西。以下是我试图实现的事情的一个例子(奇怪的例子,抱歉!(:

(def my-book
{:intro {:one-liners [{:line "wowza" :rating 7} 
{:line "cool!" :rating 4}]}
:how-many-lines 10
:rubbish-one-liners [{:line "bongo" :rating 1}
{:line "foo"   :rating 2}]
:other-info {:author {:name "me" :age 24}}})
(def my-schema
[{:intro [{:one-liners {:values [:line :rating]}}
{:values [:how-many-lines]}
{:rubbish-one-liners {:values [:line]}}
{:other-info {:author {:values [:name]}}}]}])
;;expected output when filtering via the schema:
(def my-output
{:intro {:one-liners [{:line "wowza" :rating 7} 
{:line "cool!" :rating 4}]}
:how-many-lines 10
:rubbish-one-liners [{:line "bongo"}
{:line "foo"}]
:other-info {:author {:name "me"}}})

我真的不确定这个模式是否是实现它/结构的最佳方式,一旦我有了一个模式,考虑到似乎涉及到很多不同的数据结构,该如何实际实现它——所以我想我的问题是,你会建议我如何遍历我拥有的任何结构,并基于模式删除键?:(谢谢

Clojure有选择键,您只需要对嵌套结构使用递归。试试这样的东西:

(defn key-filter [obj schema]
(cond 
(vector? obj) (map #(select-keys % schema) obj)
(map? obj) 
(let [nw (select-keys obj (keys schema))
res (map key-filter (vals nw) (vals schema))]
(zipmap (keys nw) res))
:else obj))
(def my-book {:intro {:one-liners [{:line "wowza", :rating 7} {:line "cool!", :rating 4}],
:how-many-lines 10,
:rubbish-one-liners [{:line "bongo", :rating 1} {:line "foo", :rating 2}],
:other-info {:author {:name "me", :age 24}}}})
(def my-schema {:intro {:one-liners [:line], 
:how-many-lines [],
:rubbish-one-liners [:line], 
:other-info {:author {:name []}}}})
(key-filter my-book my-schema)

没有一个全面的库可以接受模式并通过删除额外的键等方式强制数据结构与模式匹配;你的书结构,比如哪些字段是矢量或不是矢量。

首先,我将使用Specter库,只需编写所需的转换代码。一个例子:

(ns tst.demo.core
(:use tupelo.core tupelo.test)
(:require
[com.rpl.specter :as sp]))
(dotest
(let [book     {:intro              {:one-liners [{:line "wowza" :rating 7}
{:line "cool!" :rating 4}]}
:how-many-lines     10
:rubbish-one-liners [{:line "bongo" :rating 1}
{:line "foo" :rating 2}]
:other-info         {:author {:name "me" :age 24}}}
expected {:intro              {:one-liners [{:line "wowza" :rating 7}
{:line "cool!" :rating 4}]}
:how-many-lines     10
:rubbish-one-liners [{:line "bongo"}
{:line "foo"}]
:other-info         {:author {:name "me"}}}]
(is= expected
(it-> book
(sp/setval [:rubbish-one-liners sp/ALL :rating] sp/NONE it)
(sp/setval [:other-info :author :age] sp/NONE it)))))

这个代码的一个实际例子可以在这里看到。

请注意,Specter定义了DSL,它本质上是一种独立的编程语言。斯佩克特是非常强大的,但它需要一点练习和试验&错误以正确学习它(类似于学习正则表达式语法(。

您也可以使用图珀洛森林库执行此任务。

最新更新