如何按值按地图分组过滤地图集合



假设我有一个集合,比如:

(def xs 
[{:name "Apple" :type "Fruit is a type"} 
{:name "Tomato" :type "Vegetable are food"} 
{:name "Pear" :type "the type can also be Fruit"} 
{:name "Steak" :type "eat less Meat"}])

我想按集合进行过滤和分组,形成这样的内容:

{:Fruit [{:name "Apple" :type "Fruit is a type"} {:name "Pear" :type "the type can also be Fruit"}] :Vegetable [{:name "Tomato" :type "Vegetable are food"}]

我目前只是过滤结果,但似乎找不到一个好的分组方法。到目前为止,我拥有的是:

(defn filter-response [x query]
(filter #(s/includes? (:type %) query) x))
(defn group-by-types [queries]
(map #(filter-response xs %) queries))
(group-by-types ["Fruit" "Vegetable"])

我怎样才能做到这一点?

更新的答案

您可以使用列表理解来检查集合中每个项目的每个模式。

(defn- all-occurrences [xs patterns]
(for [x xs
pattern patterns
:when (clojure.string/includes? (:type x) pattern)]
[(keyword pattern) x]))

或者使用filter-response函数:

(defn- all-occurrences [xs patterns]
(for [pattern patterns
x (filter-response xs pattern)]
[(keyword pattern) x]))

然后使用reduce with update将出现的列表合并到一个单独的地图中:

(defn group-by-patterns [xs patterns]
(reduce (fn [m [pattern text]] (update m pattern conj text))
{}
(all-occurrences xs patterns)))

用新的输入调用它:

(def xs
[{:name "Apple" :type "Fruit is a type"}
{:name "Tomato" :type "Vegetable are food"}
{:name "Pear" :type "the type can also be Fruit"}
{:name "Steak" :type "eat less Meat"}])
(group-by-patterns xs ["Fruit" "Vegetable"])
=> {:Fruit ({:name "Pear", :type "the type can also be Fruit"} {:name "Apple", :type "Fruit is a type"}),
:Vegetable ({:name "Tomato", :type "Vegetable are food"})}

原始答案

首先,您可以使用分组依据对指定键下的值进行分组:

(def xs
[{:name "Apple" :type "Fruit"}
{:name "Tomato" :type "Vegetable"}
{:name "Pear" :type "Fruit"}
{:name "Steak" :type "Meat"}])
erdos=> (group-by :type xs)
{"Fruit" [{:name "Apple", :type "Fruit"} {:name "Pear", :type "Fruit"}], 
"Vegetable" [{:name "Tomato", :type "Vegetable"}],
"Meat" [{:name "Steak", :type "Meat"}]}

然后使用选择键过滤密钥:

erdos=> (select-keys (group-by :type xs) ["Fruit" "Vegetable"])
{"Fruit" [{:name "Apple", :type "Fruit"} {:name "Pear", :type "Fruit"}], 
"Vegetable" [{:name "Tomato", :type "Vegetable"}]}

如果你需要关键字密钥,你需要一个额外的映射步骤:

erdos=> (into {}
(for [[k v] (select-keys (group-by :type xs) ["Fruit" "Vegetable"])]
[(keyword k) v]))
{:Fruit [{:name "Apple", :type "Fruit"} {:name "Pear", :type "Fruit"}], 
:Vegetable [{:name "Tomato", :type "Vegetable"}]}

最新更新