2014-09-04 63 views
1

什麼是更新矢量內的地圖的clojure方式如果我有這樣的事情,假設每個地圖都有獨特的:nameclojure的方式來更新矢量內的地圖

(def some-vec 
    [{:name "foo" 
    ....} 
    {:name "bar" 
    ....} 
    {:name "baz" 
    ....}]) 

我想以某種方式更新地圖,如果有:name等於foo。目前我使用map,這樣

(map (fn [{:keys [name] :as value}] 
     (if-not (= name "foo") 
     value 
     (do-something .....))) some-vec) 

但通過整個向量這將循環,即使我只更新一個項目。

+2

因爲它是一個載體,需要循環遍吧,找你的元素,或者您可以knoy它的指數和指數接取它。你可以停止循環,當你發現你的元素,如果你知道它不會出現兩次。可能只是矢量不是你的數據類型,嘗試將其轉換爲映射和按鍵訪問。 – coredump 2014-09-04 12:06:09

回答

6

保留數據作爲地圖而不是地圖記錄的向量,以:name爲關鍵字。

(def some-data 
    {"foo" {:name "foo" :other :stuff} 
    "bar" {:name "bar" :other :stuff} 
    "baz" {:name "baz" :other :stuff}}) 

然後

(assoc-in some-data ["bar" :other] :things) 

一氣呵成產生

{"foo" {:other :stuff, :name "foo"}, 
"bar" {:other :things, :name "bar"}, 
"baz" {:other :stuff, :name "baz"}} 

您可以捕捉

(defn assoc-by-fn [data keyfn datum] 
    (assoc data (keyfn datum) datum)) 

基本操作時,例如,

(assoc-by-fn some-data :name {:name "zip" :other :fassner}) 

產生

{"zip" {:other :fassner, :name "zip"}, 
"foo" {:other :stuff, :name "foo"}, 
"bar" {:other :stuff, :name "bar"}, 
"baz" {:other :stuff, :name "baz"}} 
1

鑑於你有一個地圖矢量,你的代碼對我來說看起來很好。您對「遍歷整個向量」的擔憂是您正在對:name進行線性搜索以及向量不可變的事實的自然結果。

我想知道你真正想要的是地圖矢量嗎?爲什麼不是地圖的地圖?

(def some-map 
    {"foo" {...} 
    "bar" (...} 
    "baz" {...}} 

然後你可以用update-in更新?

1

鑑於這種形狀的輸入數據的,除非你有一個索引,可以告訴你哪些索引的地圖具有給定值爲:name的地圖噸,你將不得不遍歷整個向量。你可以,但是,減少工作中涉及的僅「更新」匹配的地圖製作更新的載體,而不是重建整個矢量量:

(defn update-values-if 
    "Assumes xs is a vector. Will update the values for which 
    pred returns true." 
    [xs pred f] 
    (let [lim (count xs)] 
    (loop [xs xs i 0] 
     (if (< i lim) 
     (let [x (nth xs i)] 
      (recur (if (pred x) 
        (assoc xs i (f x)) 
        xs) 
       (inc i))) 
     xs)))) 

因爲有值這個將執行儘可能多的assoc操作在xs中,pred返回真值。

例子:

(def some-vec [{:name "foo" :x 0} {:name "bar" :x 0} {:name "baz" :x 0}]) 

(update-values-if some-veC#(= "foo" (:name %)) #(update-in % [:x] inc)) 
;= [{:name "foo", :x 1} {:name "bar", :x 0} {:name "baz", :x 0}] 

當然,如果你打算用一些規律來改造載體以這種方式,那麼縮略圖的和保羅的建議,使用地圖的地圖將是一個更顯著的改善。如果:name沒有唯一標識地圖,則情況仍然如此 - 在這種情況下,您可以使用frequencies簡單地轉換原始矢量,並處理矢量地圖(具有給定的:name的地圖)。

0

如果您使用的是向量,您應該知道要更改的元素的索引,否則您必須以某種方式遍歷它。

我可以提出此解決方案:

(defn my-update [coll val fnc & args] 
    (let [index (->> (map-indexed vector coll) 
        (filter (fn [[_ {x :name}]] (= x val))) 
        ffirst)] 
    (when index 
     (apply update-in coll [index] fnc args)))) 

其中:
coll - 給出地圖的收集; val - 字段值:name; fnc - 更新功能; args - 更新函數的參數。

讓我們試一下:

user> (def some-vec 
     [{:name "foo"} 
     {:name "bar"} 
     {:name "baz"}]) 
;; => #'user/some-vec 
user> (my-update some-vec "foo" assoc :boo 12) 
;; => [{:name "foo", :boo 12} {:name "bar"} {:name "baz"}] 
user> (my-update some-vec "bar" assoc :wow "wow!") 
;; => [{:name "foo"} {:name "bar", :wow "wow!"} {:name "baz"}] 

我認爲Thumbnail's answer可能對你非常有用。如果您可以將數據保存爲地圖,則這些操作變得更加容易。這裏是你可以如何改變你的載體引入地圖:

user> (apply hash-map (interleave (map :name some-vec) some-vec)) 
;; => {"foo" {:name "foo"}, "bar" {:name "bar"}, "baz" {:name "baz"}} 
+0

謝謝,馬克。我會去'(減少#(assoc-by-fn%1:name%2){} some-vector)'來加載地圖。這可能值得通過':name'參數來結束,這樣表格就可以被一個函數操縱。 – Thumbnail 2014-09-04 13:51:01

相關問題