简体   繁体   English

如何遍历复杂的 map 并根据 Clojure 中的模式删除密钥?

[英]How do I walk a complex map and remove keys based on a schema in Clojure?

I'm fairly new to Clojure so apologies if this is a stupid question, but I have a map which is fairly complex (it contains nested maps, vectors etc.).我对 Clojure 还很陌生,如果这是一个愚蠢的问题,我很抱歉,但我有一个相当复杂的 map(它包含嵌套地图、向量等)。 Before I put it into my database, I essentially want to dissoc a bunch of things I don't need to save write speed (in this case it's a significant performance improvement).在我将它放入我的数据库之前,我基本上想解开一堆我不需要保存写入速度的东西(在这种情况下,这是一个显着的性能改进)。 I'd like to filter out all the bits I don't need by making a schema of some sort which could be used to remove anything unnecessary.我想通过制作某种模式来过滤掉我不需要的所有位,这种模式可用于删除任何不必要的东西。 Here's an example of the sort of thing I'm trying to achieve (weird example, apologies:):这是我试图实现的那种事情的一个例子(奇怪的例子,道歉:):

(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"}}})

I'm not really sure if that schema is at all the best way to go about it/structure, and once I do have a schema how to actually go about it given there seems to be a lot of different data structures involved - so I guess my question is, how would you advise I go about walking through whatever structure I have and removing keys based on a schema?我不确定该架构是否是 go 关于它/结构的最佳方式,一旦我有一个架构,如何实际 go 考虑到它似乎涉及很多不同的数据结构 - 所以我猜我的问题是,你会如何建议我 go 关于遍历我拥有的任何结构并根据模式删除键? :) thanks! :) 谢谢!

Clojure has select-keys , you just need to use recursion for nested structures. Clojure 有选择键,你只需要对嵌套结构使用递归。 Try something like this:尝试这样的事情:

(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)

There is no comprehensive library that will accept a schema and coerce a data structure to match the schema by dropping extra keys, etc. Also, you have some inconsistencies between your schema & your book structure, such as which fields are vectors or not.没有全面的库可以接受架构并通过删除额外的键等来强制数据结构与架构匹配。此外,您的架构和书籍结构之间存在一些不一致之处,例如哪些字段是向量或不是向量。

To start, I would use the Specter library and just hand-code up the desired transformations.首先,我会使用 Spectre 库并手动编写所需的转换。 An example:一个例子:

(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)))))

A live example of this code can be seen here . 可以在此处查看此代码的实时示例。

Note that Specter defines a DSL that is, essentially, a separate programming language.请注意,Spectre 定义了一种 DSL,它本质上是一种单独的编程语言。 Specter is very powerful, but it takes a little practice and trial & error to learn it properly (similar to learning Regular Expression syntax). Spectre 非常强大,但需要一些练习和反复试验才能正确学习(类似于学习正则表达式语法)。

You could also usethe Tupelo Forest library for this task.您还可以使用Tupelo Forest 库来完成此任务。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM