在 Clojure 中,如何对元素进行分组?

发布于 2025-01-08 03:56:19 字数 581 浏览 2 评论 0原文

在 clojure 中,我想聚合这些数据:

(def data [[:morning :pear][:morning :mango][:evening :mango][:evening :pear]])
(group-by first data)
;{:morning [[:morning :pear][:morning :mango]],:evening [[:evening :mango][:evening :pear]]}

我的问题是 :evening:morning 是多余的。 相反,我想创建以下集合:

([:morning (:pear :mango)] [:evening (:mango :pear)])

我想出了:

(for [[moment moment-fruit-vec] (group-by first data)] [moment (map second moment-fruit-vec)])

是否有更惯用解决方案?

In clojure, I want to aggregate this data:

(def data [[:morning :pear][:morning :mango][:evening :mango][:evening :pear]])
(group-by first data)
;{:morning [[:morning :pear][:morning :mango]],:evening [[:evening :mango][:evening :pear]]}

My problem is that :evening and :morning are redundant.
Instead, I would like to create the following collection:

([:morning (:pear :mango)] [:evening (:mango :pear)])

I came up with:

(for [[moment moment-fruit-vec] (group-by first data)] [moment (map second moment-fruit-vec)])

Is there a more idiomatic solution?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

放肆 2025-01-15 03:56:19

我也遇到过类似的分组问题。通常我最终会将 merge-with 或 update-in 插入到某个 seq 处理步骤中:

(apply merge-with list (map (partial apply hash-map) data))

您会得到一个映射,但这只是一个键值对的 seq:

user> (apply merge-with list (map (partial apply hash-map) data))
{:morning (:pear :mango), :evening (:mango :pear)}
user> (seq *1)
([:morning (:pear :mango)] [:evening (:mango :pear)])

但是,如果每个键出现两次,此解决方案只能得到您想要的内容。这可能会更好:

(reduce (fn [map [x y]] (update-in map [x] #(cons y %))) {} data)

这两者都感觉“更实用”,但也感觉有点复杂。不要太快地否定您的解决方案,它很容易理解并且功能足够。

I've come across similar grouping problems. Usually I end up plugging merge-with or update-in into some seq processing step:

(apply merge-with list (map (partial apply hash-map) data))

You get a map, but this is just a seq of key-value pairs:

user> (apply merge-with list (map (partial apply hash-map) data))
{:morning (:pear :mango), :evening (:mango :pear)}
user> (seq *1)
([:morning (:pear :mango)] [:evening (:mango :pear)])

This solution only gets what you want if each key appears twice, however. This might be better:

(reduce (fn [map [x y]] (update-in map [x] #(cons y %))) {} data)

Both of these feel "more functional" but also feel a little convoluted. Don't be too quick to dismiss your solution, it's easy-to-understand and functional enough.

尹雨沫 2025-01-15 03:56:19

不要太快地关闭 group-by,它已按所需的键聚合您的数据,并且它没有更改数据。任何其他需要矩-果对序列的函数都将接受在 group-by 返回的映射中查找的任何值。

在计算摘要方面,我倾向于使用merge-with,但为此我必须将输入数据转换为一系列映射,并使用所需的键和构造一个“基本映射”空向量作为值。

(let [i-maps (for [[moment fruit] data] {moment fruit})
      base-map (into {} 
                  (for [key (into #{} (map first data))] 
                    [key []]))]
      (apply merge-with conj base-map i-maps))

{:morning [:pear :mango], :evening [:mango :pear]}

Don't be too quick to dismiss group-by, it has aggregated your data by the desired key and it hasn't changed the data. Any other function expecting a sequence of moment-fruit pairs will accept any value looked up in the map returned by group-by.

In terms of computing the summary my inclination was to reach for merge-with but for that I had to transform the input data into a sequence of maps and construct a "base-map" with the required keys and empty-vectors as values.

(let [i-maps (for [[moment fruit] data] {moment fruit})
      base-map (into {} 
                  (for [key (into #{} (map first data))] 
                    [key []]))]
      (apply merge-with conj base-map i-maps))

{:morning [:pear :mango], :evening [:mango :pear]}
原谅过去的我 2025-01-15 03:56:19

思考 @mike t 的答案,我想出了:

(defn agg[x y] (if (coll? x) (cons y x) (list y x)))
(apply merge-with agg (map (partial apply hash-map) data))

当按键出现时,此解决方案也有效data 上的次数超过两次:

 (apply merge-with agg (map (partial apply hash-map) 
     [[:morning :pear][:morning :mango][:evening :mango] [:evening :pear] [:evening :kiwi]]))
;{:morning (:mango :pear), :evening (:kiwi :pear :mango)}

Meditating on @mike t's answer, I've come up with:

(defn agg[x y] (if (coll? x) (cons y x) (list y x)))
(apply merge-with agg (map (partial apply hash-map) data))

This solution works also when the keys appear more than twice on data:

 (apply merge-with agg (map (partial apply hash-map) 
     [[:morning :pear][:morning :mango][:evening :mango] [:evening :pear] [:evening :kiwi]]))
;{:morning (:mango :pear), :evening (:kiwi :pear :mango)}
鸠魁 2025-01-15 03:56:19

也许只需稍微修改一下标准分组:

(defn my-group-by 
  [fk fv coll]  
  (persistent!
   (reduce
    (fn [ret x]
      (let [k (fk x)]
        (assoc! ret k (conj (get ret k []) (fv x)))))
    (transient {}) coll)))

然后将其用作:

(my-group-by first second data)

maybe just modify the standard group-by a little bit:

(defn my-group-by 
  [fk fv coll]  
  (persistent!
   (reduce
    (fn [ret x]
      (let [k (fk x)]
        (assoc! ret k (conj (get ret k []) (fv x)))))
    (transient {}) coll)))

then use it as:

(my-group-by first second data)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文