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

How to filter a collection of maps into group-by map by value?

假设我有一个像这样的集合:

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

我怎样才能做到这一点?

更新答案

您可以使用 list comprehension 检查集合中每个项目的每个模式。

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

原答案

首先,您可以使用 group-by 按指定键下的值进行分组:

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

然后用select-keys过滤keys:

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"}]}