如何按Clojure中已分组的集合进行分组?

时间:2016-07-20 22:03:39

标签: clojure

我有一组地图

(def a '({:id 9345 :value 3 :type "orange"}
         {:id 2945 :value 2 :type "orange"}
         {:id 145 :value 3 :type "orange"}
         {:id 2745 :value 6 :type "apple"}
         {:id 2345 :value 6 :type "apple"}))

我想先按值分组,然后按类型分组。

我的输出应该如下:

{
    :orange [{
        :value 3,
        :id [9345, 145]
    }, {
        :value 2,
        :id [2935]
    }], 
    :apple [{
        :value 6,
        :id [2745, 2345]
    }]
}

我如何在Clojure中做到这一点?感谢您的回答。

谢谢!

修改:

这是我到目前为止所做的:

(defn by-type-key [data]
  (group-by #(get % "type") data))

(reduce-kv
  (fn [m k v] (assoc m k (reduce-kv
                           (fn [sm sk sv] (assoc sm sk (into [] (map #(:id %) sv))))
                           {}
                           (group-by :value (map #(dissoc % :type) v)))))
  {}
  (by-type-key a))

输出:

=> {"orange" {3 [9345 145], 2 [2945]}, "apple" {6 [2745 2345], 3 [125]}}

我无法弄清楚如何继续......

3 个答案:

答案 0 :(得分:2)

您的要求有点不一致(或者说不规则) - 您在结果中使用:type值作为关键字,但其余的关键字都是通过的。也许你需要做些什么才能满足一些外部格式 - 否则你需要使用与:type相同的方法,或者在结果中添加新的关键字,如:group:rows并保持原始关键字的完整性。我将暂时采用前一种方法(但请参见下文,我将根据您的需要得到形状),因此最终的数据形状就像

{:orange
     {:3 [9345 145],
      :2 [2945]},
 :apple
     {:6 [2745 2345]}
}

到达那里的方法不止一种,这里有一个要点:

(group-by (juxt :type :value) a)

结果:

{["orange" 3] [{:id 9345, :value 3, :type "orange"} {:id  145, :value 3, :type "orange"}],
 ["orange" 2] [{:id 2945, :value 2, :type "orange"}],
 ["apple" 6] [{:id 2745, :value 6, :type "apple"} {:id 2345, :value 6, :type "apple"}]}

现在,您的集合中的所有行都按您需要的键分组。从这里,你可以去获得你想要的形状,比如说你可以做到上面的形状

(reduce 
 (fn [m [k v]]
    (let [ks (map (comp keyword str) k)]
      (assoc-in m ks
                (map :id v))))
 {}
 (group-by (juxt :type :value) a))

基本思路是按键序列(以及group-byjuxt执行的操作)对行进行分组,然后合并reduce和{{1 }或assoc-in将结果击败到位。

要准确获得您描述的形状:

update-in

它有点吵,可能更难看到树木的森林 - 这就是为什么我简化形状,突出主要想法。你的形状越规则,你的功能越短越规则 - 所以如果你能控制它,试着让它更简单。

答案 1 :(得分:1)

您可能希望使用内置函数{{1}}。见http://clojuredocs.org/clojure.core/group-by

答案 2 :(得分:1)

我会分两个阶段进行转换(使用reduce):

  • 第一个收集值
  • 第二次格式化

以下代码解决了您的问题:

(def a '({:id 9345 :value 3 :type "orange"}
         {:id 2945 :value 2 :type "orange"}
         {:id 145 :value 3 :type "orange"}
         {:id 2745 :value 6 :type "apple"}
         {:id 2345 :value 6 :type "apple"}))

(defn standardise [m]
  (->> m
       ;; first stage
       (reduce (fn [out {:keys [type value id]}]
                 (update-in out [type value] (fnil #(conj % id) [])))
               {})
       ;; second stage
       (reduce-kv (fn [out k v]
                    (assoc out (keyword k)
                           (reduce-kv (fn [out value id]
                                        (conj out {:value value
                                                   :id id}))
                                      []
                                      v)))
                  {})))

(standardise a)
;; => {:orange [{:value 3, :id [9345 145]} 
;;              {:value 2, :id [2945]}], 
;;     :apple [{:value 6, :id [2745 2345]}]}

第一阶段的输出是:

(reduce (fn [out {:keys [type value id]}]
             (update-in out [type value] (fnil #(conj % id) [])))
        {}
        a)
;;=> {"orange" {3 [9345 145], 2 [2945]}, "apple" {6 [2745 2345]}}