2010-02-04 27 views
12

Tôi có thể đặt chức năng nào làm FOO ở đây để mang lại giá trị đúng vào cuối? Tôi đã chơi với bộ băm (chỉ đúng cho 2 giá trị đầu tiên), conj, và concat nhưng tôi biết tôi không xử lý đơn nguyên tố vs set condition đúng với bất kỳ điều nào trong số đó.Hợp nhất danh sách bản đồ và kết hợp các giá trị với các bộ trong Clojure

(defn mergeMatches [propertyMapList] 
    "Take a list of maps and merges them combining values into a set" 
    (reduce #(merge-with FOO %1 %2) {} propertyMapList)) 

(def in 
    (list 
     {:a 1} 
     {:a 2} 
     {:a 3} 
     {:b 4} 
     {:b 5} 
     {:b 6})) 

(def out 
    { :a #{ 1 2 3} 
     :b #{ 4 5 6} }) 

; this should return true 
(= (mergeMatches in) out) 

Cách thành ngữ nhất để xử lý vấn đề này là gì?

Trả lời

12

này sẽ thực hiện:

(let [set #(if (set? %) % #{%})] 
    #(clojure.set/union (set %) (set %2))) 

Viết lại trực tiếp hơn cho các ví dụ (Alex):

(defn to-set [s] 
    (if (set? s) s #{s})) 
(defn set-union [s1 s2] 
    (clojure.set/union (to-set s1) (to-set s2))) 
(defn mergeMatches [propertyMapList] 
    (reduce #(merge-with set-union %1 %2) {} propertyMapList)) 
+0

a) do hạn chế không có chức năng ẩn danh lồng nhau, nó cũng không hợp lệ. b) không quá đẹp! :) –

+0

Ngoài ra, tôi nghĩ rằng điều này sẽ trả về một bộ bản đồ thay vì bản đồ với các giá trị đã đặt. –

+1

@ Alex: a) Không có giới hạn nào như vậy. Bạn không thể lồng hàm ẩn danh * literals * (được tạo bằng '# (...)'), nhưng bạn có thể lồng các hàm ẩn danh (mặc dù bạn sẽ cần sử dụng 'fn'). b) Một vấn đề của hương vị, tôi đoán. :-) Ngoài ra, bạn nên thử thực sự chạy nó, vì nó xảy ra chức năng nó tạo ra thực sự làm cho biểu thức kiểm tra của bạn trả về 'true' khi thay thế thay cho' FOO' trong biểu thức 'reduce' của bạn. –

4

tôi sẽ không sử dụng kết hợp với các cho điều này,

(defn fnil [f not-found] 
    (fn [x y] (f (if (nil? x) not-found x) y))) 
(defn conj-in [m map-entry] 
    (update-in m [(key map-entry)] (fnil conj #{}) (val map-entry))) 
(defn merge-matches [property-map-list] 
    (reduce conj-in {} (apply concat property-map-list))) 

user=> (merge-matches in) 
{:b #{4 5 6}, :a #{1 2 3}} 

fnil sẽ là một phần của lõi sớm để bạn có thể bỏ qua việc thực hiện ... nhưng nó chỉ tạo ra một phiên bản của một hàm khác có thể xử lý nil arg uments. Trong trường hợp này conj sẽ thay thế # {} cho nil.

Vì vậy, việc giảm liên kết với tập hợp cho mỗi khóa/giá trị trong danh sách các bản đồ được cung cấp.

+0

Tôi thích điều này như một giải pháp và nó sử dụng những thứ đang chuyển vào các thư viện lõi. Giải pháp từ @amitrathore là ý tưởng cơ bản giống nhau nhưng đơn giản hơn. Tôi vẫn chưa quyết định liệu nó có thực sự có chức năng khác nhau hay không. –

+0

Vì vậy, sự khác biệt là các giải pháp khác tạo ra một bản đồ của danh sách, không phải là một bản đồ của bộ đó không phải là tốt cho tôi. –

+0

Khi sử dụng thêm, vấn đề với giải pháp này là nó không giải quyết trường hợp các bản đồ đến đã có các giá trị như là các giá trị. (Có, tôi thêm yêu cầu trễ. :) Ví dụ: người dùng => (println (mergeMatches (danh sách {: a # {1 2 3}} {: a # {4 5 6}}))) {: a # {# {1 2 3} # {4 5 6}}} khi tôi thực sự muốn {: a # {1 2 3 4 5 6}} như vậy. \t (println (mergeMatches in2)) –

2

Không siêu đẹp nhưng nó hoạt động.

(defn mergeMatches [propertyMapList] 
    (for [k (set (for [pp propertyMapList] (key (first pp))))] 
     {k (set (remove nil? (for [pp propertyMapList] (k pp))))})) 
+0

Rất thông minh. Tôi hơi cảnh giác với việc duy trì mã bằng cách sử dụng nhiều tính năng danh sách lồng nhau !! :) Tôi nghĩ rằng một số giải pháp khác có thể đọc được và dễ hiểu hơn một chút. –

+0

Vâng, nó trở thành một chút dày đặc, nhưng tôi chưa bao giờ hiểu tại sao nó không được sử dụng thường xuyên hơn trong Clojure. Xem http://www.bestinclass.dk/index.php/2010/02/clojure-list-comprehension/ để biết thêm một số ví dụ gọn gàng hơn. – mac

2

Điều này dường như làm việc:

(defn FOO [v1 v2] 
     (if (set? v1) 
      (apply hash-set v2 v1) 
      (hash-set v1 v2))) 
+0

Tương tự như @Chas nhưng đơn giản hơn bằng cách tránh liên minh. –

4

tôi không viết những dòng này nhưng nó đã contributed bởi @amitrathore trên Twitter:

(defn kv [bag [k v]] 
    (update-in bag [k] conj v)) 
(defn mergeMatches [propertyMapList] 
    (reduce #(reduce kv %1 %2) {} propertyMapList)) 
+0

Tóm lại, tôi nghĩ đây là giải pháp đơn giản và dễ hiểu nhất. Cũng dựa vào việc cập nhật như câu trả lời của @Timothy Pratley. Tôi nghĩ vấn đề chính với vấn đề này là nó tạo ra một bản đồ các danh sách thay vì một bản đồ các bộ, điều này không cho tôi sự phân chia mà tôi cần. –

+0

thay đổi 'conj' by' (fnil conj # {}) 'làm thủ thuật – DanLebrero

3

Một giải pháp góp của @wmacgyver trên Twitter dựa trên multimaps:

(defn add 
    "Adds key-value pairs the multimap." 
    ([mm k v] 
    (assoc mm k (conj (get mm k #{}) v))) 
    ([mm k v & kvs] 
    (apply add (add mm k v) kvs))) 
(defn mm-merge 
    "Merges the multimaps, taking the union of values." 
    [& mms] 
    (apply (partial merge-with union) mms)) 

(defn mergeMatches [property-map-list] 
    (reduce mm-merge (map #(add {} (key (first %)) (val (first %))) property-map-list)))  
+1

Tôi thích ý tưởng xây dựng một cách rõ ràng (hoặc sử dụng) một thư viện multimap để xử lý cấu trúc dữ liệu này và đơn giản dựa vào nó. Tùy thuộc vào bao nhiêu tôi kết thúc bằng cách sử dụng nó, tôi thực sự có thể đi theo hướng này lâu dài. –

Các vấn đề liên quan