2012-02-22 42 views
10

Trong clojure, tôi muốn tổng hợp dữ liệu này:Trong Clojure, cách nhóm các phần tử?

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

Vấn đề của tôi là :evening:morning là không cần thiết. Thay vào đó, tôi muốn tạo ra bộ sưu tập sau:

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

tôi đến với:

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

Có nhiều giải pháp ngữ?

+1

Tên biến trong bạn giải pháp đề xuất là gây hiểu nhầm. Giá trị bị phá hủy là 'quả' thực sự là một chuỗi các vectơ cặp trái cây thời điểm. –

+0

Cảm ơn rất nhiều! Cập nhật câu hỏi – viebel

Trả lời

5

Tôi đã gặp các vấn đề về nhóm tương tự. Thông thường tôi kết thúc cắm ghép-có hoặc cập nhật-in vào một số bước xử lý seq:

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

Bạn nhận được một bản đồ, nhưng đây chỉ là một seq của cặp khóa-giá trị:

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

này giải pháp chỉ nhận được những gì bạn muốn nếu mỗi khóa xuất hiện hai lần, tuy nhiên. Điều này có thể tốt hơn:

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

Cả hai cảm giác này "có chức năng hơn" nhưng cũng cảm thấy hơi phức tạp. Đừng quá nhanh để loại bỏ giải pháp của bạn, nó rất dễ hiểu và đủ chức năng.

+1

Bạn nghĩ gì về '(áp dụng hợp nhất-với (comp flatten list) (bản đồ (áp dụng một phần bản đồ băm)))? – viebel

+1

Đó là một sửa chữa tốt, súc tích. Tôi tin rằng 'flatten' là ** O (n) **, vì vậy nó có thể không hoạt động tốt liên tục áp dụng nó trong bộ dữ liệu nhất định. –

+1

Bạn nói đúng. Tôi tìm thấy một giải pháp tốt hơn, xem câu trả lời của tôi. BTW là có bất kỳ chức năng được xây dựng trong đó không giống như 'agg'? – viebel

4

Đừng quá nhanh để loại bỏ group-by, nó đã tổng hợp dữ liệu của bạn theo khóa mong muốn và nó đã không thay đổi dữ liệu. Bất kỳ chức năng nào khác mong đợi một chuỗi các cặp thời gian-trái cây sẽ chấp nhận bất kỳ giá trị nào được tra cứu trong bản đồ được trả về bởi group-by.

Về tính toán tóm tắt, độ nghiêng của tôi là để đạt được số merge-with nhưng tôi phải chuyển dữ liệu đầu vào thành một chuỗi bản đồ và xây dựng "bản đồ cơ sở" với các khóa bắt buộc và vectơ trống làm giá trị .

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

Suy ngẫm về câu trả lời @mike t 's, tôi đã đưa ra:

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

giải pháp này hoạt động cũng có khi các phím xuất hiện nhiều hơn hai lần trên 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)} 
Các vấn đề liên quan