2012-08-01 21 views
11

Cách thức thành ngữ để kiểm tra xem một khóa trên bản đồ có giá trị không? Ví dụ, nếu chúng ta có:Cách thức thành ngữ để kiểm tra xem một khóa trên bản đồ có giá trị là

=> (def seq-of-maps [{:foo 1 :bar "hi"} {:foo 0 :bar "baz"}]) 

Để tìm hiểu tất cả các bản đồ với: foo == 0, tôi thích:

=> (filter (comp zero? :foo) seq-of-maps) 
({:foo 0, :bar "baz"}) 

Nhưng nếu tôi muốn tìm tất cả các bản đồ với: thanh == "Xin chào", điều tốt nhất mà tôi có thể nghĩ đến là:

=> (filter #(= (:bar %) "hi") seq-of-maps) 
({:foo 1, :bar "hi"}) 

mà tôi không thấy dễ đọc. Có cách nào tốt hơn/thành ngữ hơn để làm điều đó không?

Trả lời

4

Tôi cá nhân như refactoring loại điều để sử dụng một hàm bậc cao được đặt tên rõ ràng:

(def seq-of-maps [{:foo 1 :bar "hi"} {:foo 0 :bar "baz"}]) 

(defn has-value [key value] 
    "Returns a predicate that tests whether a map contains a specific value" 
    (fn [m] 
    (= value (m key)))) 

(filter (has-value :bar "hi") seq-of-maps) 
=> ({:foo 1, :bar "hi"}) 

Nhược điểm là nó cung cấp cho bạn một định nghĩa chức năng thêm để quản lý và duy trì, nhưng tôi nghĩ rằng sự sang trọng/code khả năng đọc là giá trị nó. Cách tiếp cận này cũng có thể rất hiệu quả từ góc nhìn hiệu suất nếu bạn sử dụng lại biến vị ngữ nhiều lần.

+0

Ý tưởng hay, tôi thích nó. Cũng giống như một bên, trong mã, cuộc gọi cuối cùng của bạn dường như không khớp với 'seq-of-maps' mà bạn xác định trong dòng đầu tiên. –

+0

Rất tiếc trên bản sao/dán, đã khắc phục! – mikera

9

Idiomatic là chủ quan, nhưng tôi muốn làm

=> (filter (comp #{"hi"} :bar) seq-of-maps) 

hoặc những gì bạn đã làm.

1

Ví dụ thứ ba về việc chuyển một hàm ẩn danh để lọc có vẻ giống như một trong các phương pháp không chính xác để tìm bản đồ có giá trị nhất định. Tôi thấy nó khá dễ đọc.

3

clojure.set/index cũng có thể được sử dụng ở đây

((index seq-of-maps [:foo]) {:foo 0}) 
((index seq-of-maps [:bar]) {:bar "hi"}) 

Nếu bạn muốn, bạn có thể bọc nó trong một hàm

(defn select-maps [xrel m] 
    ((index xrel (keys m)) m)) 

sau đó

(select-maps seq-of-maps {:foo 0}) 
(select-maps seq-of-maps {:bar "hi"}) 

cả công việc - bạn cũng có thể yêu cầu bản đồ có nhiều khóa/giá trị sử dụng chỉ mục:

(select-maps seq-of-maps {:foo 0 :bar "baz"}) 

chọn tất cả bản đồ chứa foo 0 và thanh "baz"

+0

Suy nghĩ về vấn đề này, có vẻ như điều này không có được những hành vi chính xác mà bạn yêu cầu , vì nó sẽ trả về một tập hợp cụ thể các bản đồ duy nhất, chứ không phải một luồng có khả năng chứa các bản sao. – ChrisR

2
user> (def seq-of-maps [{:foo 1 :bar "hi"} {:foo 0 :bar "baz"}]) 
#'user/seq-of-maps 
user> (filter #(-> % :bar (= "hi")) seq-of-maps) 
({:foo 1, :bar "hi"}) 

Như Pepijn nói, tôi nghĩ rằng cách thành ngữ thay đổi theo ý kiến ​​cá nhân. Đôi khi tôi sử dụng macro -> để trích xuất các dấu ngoặc ôm lồng nhau.

0

Mã của bạn có vẻ ổn với tôi. giải pháp khả thi khác có thể được sử dụng for vĩ mô như hình dưới đây

(for [m seq-of-maps 
     :let [v (:bar m)] 
     :when (= v "hi")] 
    m) 
0

Chỉ cần thay thế zero? với (partial = "hi") như thế này:

=> (filter (comp (partial = "hi") :bar) seq-of-maps) 
Các vấn đề liên quan