2013-06-26 17 views
9

マージ:私はトラブル次のような問題の解決策を見つけることを抱えている2つの複雑なデータ構造に

(def defaults { 
    :name "John" 
    :surname "Doe" 
    :info {:date-of-birth "01-01-1980" 
      :registered [{:type "newsletter" :name "breaking news" }]} 
}) 

そして私は、同様の構造化されたマップを渡すが、私がしたい:

は、私はマップを考えてみましょうベクトルをconjoinとキーの残りの部分を上書き:

(def new { 
    :name "Peter" 
    :info {:date-of-birth "11-01-1986" 
      :registered [{:type "alert" :name "mobile-alert" }]} 
}) 

そして、私はこの結果をしたい:

{:name "Peter" 
    :surname "Doe" 
    :info {:date-of-birth "11-01-1986" 
      :registered [{:type "newsletter" :name "breaking news" } 
          {:type "alert"  :name "mobile-alert" }]}} 

今、私のような静的な構文を使用して簡単に行うことができます。

(reduce conj (get-in defaults [:info :registered]) (get-in new [:info :registered])) 

(より良い方法はおそらくあります...)しかし、私は次のプロパティを持つ動的な機能の多くを期待していた:

をキーのvalがベクトルである場合
  1. はその後、右のマップ​​
  2. からの値を持つ任意のキー構造に
  3. アップデートを知らなくても、両方のマップからすべてのキーをキープconj右のマップ​​のベクトルとベクトル(適切なキーは、もちろん存在する場合)事前に助けを

感謝:)

答えて

20

あなたは間違いなくmerge-withの機能を調べる必要があります。これは実現可能です:

(defn deep-merge [a b] 
    (merge-with (fn [x y] 
       (cond (map? y) (deep-merge x y) 
         (vector? y) (concat x y) 
         :else y)) 
       a b)) 
+0

+1完全に「merge-with」を忘れました。 –

+0

完全に動作します。私はそれがマージと何か関係があると思ったが、私はそれを理解できなかった...乾杯! –

0

ここでこの種の機能のための可能な実装があります。それは少なくとも出発点です。データの構造によっては、いくつかの追加検証が必要な場合があります(たとえば、オーバーライドマップの値がベクターで、デフォルトマップの値がコレクションでない場合など)。

(declare merge-maps) 

(defn merge-map [x [k v]] 
    (cond (vector? v) 
      (assoc x k (vec (reduce conj (x k) v))) 
     (map? v) 
      (assoc x k (merge-maps (x k) v)) 
     :esle 
      (assoc x k v))) 

(defn merge-maps [x y] 
    (reduce merge-map x y)) 

(merge-maps defaults new) 

;= {:info {:date-of-birth "11-01-1986", 
;=   :registered [{:name "breaking news", :type "newsletter"} 
;=      {:name "mobile-alert", :type "alert"}]}, 
;= :name "Peter", 
;= :surname "Doe"} 
関連する問題