【问题标题】:How to filter a collection of maps into group-by map by value?如何按值将一组地图过滤成分组地图?
【发布时间】:2021-05-02 18:02:41
【问题描述】:

假设我有一个类似的集合:

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

我怎样才能做到这一点?

【问题讨论】:

    标签: clojure clojurescript


    【解决方案1】:

    更新答案

    您可以使用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]))
    

    然后使用reduceupdate 将出现的列表合并到一个地图中:

    (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 过滤键:

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

    【讨论】:

    • 哦,这是一个很好的回应。
    • 我意识到我遗漏了一个非常重要的细节::type 键的查找是基于字符串匹配的。我的实际数据看起来像:{:type "Fruit is a type"}, {:type "The type can also be Fruit} 等。这就是我有 clojure.string/includes 的原因。我会尝试将其合并到解决方案中。
    • 哦,有道理。我已经更新了我的答案以反映这些变化。
    • 再次感谢!很好的答案
    猜你喜欢
    • 2020-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    • 1970-01-01
    • 2021-12-22
    • 2020-10-23
    相关资源
    最近更新 更多