2016-08-10 8 views
5

Map.merge के साथ मेरे पास है:इलीक्सिर - गहरे नक्शा कैसे गर्ज करें?

Map.merge(%{ a: %{ b: 1 }}, %{ a: %{ c: 3 }}) # => %{ a: %{ c: 3 }} 

लेकिन वास्तव में मैं चाहता हूँ करने के लिए:

Map.merge(%{ a: %{ b: 1 }}, %{ a: %{ c: 3 }}) # => %{ a: %{ b: 1, c: 3 }} 

वहाँ किसी भी देशी विधि इस मामले के लिए एक पुनरावर्ती बॉयलरप्लेट समारोह लेखन के बिना है?

उत्तर

10

जैसा कि @ डोगबर्ट ने सुझाव दिया है, आप नक्शे को दोबारा मर्ज करने के लिए एक फ़ंक्शन लिख सकते हैं।

defmodule MapUtils do 
    def deep_merge(left, right) do 
    Map.merge(left, right, &deep_resolve/3) 
    end 

    # Key exists in both maps, and both values are maps as well. 
    # These can be merged recursively. 
    defp deep_resolve(_key, left = %{}, right = %{}) do 
    deep_merge(left, right) 
    end 

    # Key exists in both maps, but at least one of the values is 
    # NOT a map. We fall back to standard merge behavior, preferring 
    # the value on the right. 
    defp deep_resolve(_key, _left, right) do 
    right 
    end 
end 

यहाँ कुछ परीक्षण मामलों आप इसका अंदाज़ा लगा संघर्ष हल कर रहे हैं देने के लिए कर रहे हैं:

ExUnit.start 

defmodule MapUtils.Test do 
    use ExUnit.Case 

    test 'one level of maps without conflict' do 
    result = MapUtils.deep_merge(%{a: 1}, %{b: 2}) 
    assert result == %{a: 1, b: 2} 
    end 

    test 'two levels of maps without conflict' do 
    result = MapUtils.deep_merge(%{a: %{b: 1}}, %{a: %{c: 3}}) 
    assert result == %{a: %{b: 1, c: 3}} 
    end 

    test 'three levels of maps without conflict' do 
    result = MapUtils.deep_merge(%{a: %{b: %{c: 1}}}, %{a: %{b: %{d: 2}}}) 
    assert result == %{a: %{b: %{c: 1, d: 2}}} 
    end 

    test 'non-map value in left' do 
    result = MapUtils.deep_merge(%{a: 1}, %{a: %{b: 2}}) 
    assert result == %{a: %{b: 2}} 
    end 

    test 'non-map value in right' do 
    result = MapUtils.deep_merge(%{a: %{b: 1}}, %{a: 2}) 
    assert result == %{a: 2} 
    end 

    test 'non-map value in both' do 
    result = MapUtils.deep_merge(%{a: 1}, %{a: 2}) 
    assert result == %{a: 2} 
    end 
end 
+0

बहुत बहुत धन्यवाद !!! – asiniy

+0

छोटी बात यह है कि यहां से अवगत होना यह है कि यह चुपचाप structs/कस्टम परिभाषित प्रकारों के साथ-साथ अंदर के नक्शे को मर्ज करेगा। इससे बचने के लिए आप '__struct__' कुंजी पर पैटर्न मिलान कर सकते हैं जो मौजूद है जब यह एक संरचना है। इन नुकसानों में पड़ने वाले लोगों से बचने के लिए मैंने एक [deep_merge] (https://github.com/PragTob/deep_merge) लाइब्रेरी भी लिखी। – PragTob

5

आप केवल वे एप्लिकेशन के अंदर नक्शे का 1 स्तर नेस्टिंग है, और शीर्ष स्तर के नक्शे के सभी मानों नक्शे हैं, तो आप उपयोग कर सकते हैं Map.merge/3 हैं:

iex(1)> a = %{ a: %{ b: 1 }} 
%{a: %{b: 1}} 
iex(2)> b = %{ a: %{ c: 3 }} 
%{a: %{c: 3}} 
iex(3)> Map.merge(a, b, fn _, a, b -> Map.merge(a, b) end) 
%{a: %{b: 1, c: 3}} 

अनंत घोंसले के लिए, मैं एक समारोह लिख विश्वास है एकमात्र तरीका लेकिन उस फ़ंक्शन में आप कुछ कोड को कम करने के लिए Map.merge/3 का उपयोग कर सकते हैं।

1

के रूप में सिर्फ भोली दृष्टिकोण एक टिप्पणी में उल्लेख किया है के रूप में भी गलती से सब structs/कस्टम प्रकार विलीन हो जाती है deep_merge करने के लिए वे आंतरिक रूप से नक्शे हैं। मैंने एक ही गलती की और इसलिए इन गलतियों को रोकने और आगे की सुविधाएं प्रदान करने के लिए deep_merge लाइब्रेरी लागू की।

DeepMerge.deep_merge original_map, other_map 

iex> DeepMerge.deep_merge(%{a: 1, b: %{x: 10, y: 9}}, %{b: %{y: 20, z: 30}, c: 4}) 
%{a: 1, b: %{x: 10, y: 20, z: 30}, c: 4} 

iex> DeepMerge.deep_merge([a: 1, b: [x: 10, y: 9]], [b: [y: 20, z: 30], c: 4]) 
[a: 1, b: [x: 10, y: 20, z: 30], c: 4] 

यह अतिरिक्त सुविधाओं की एक जोड़ी है आप हो सकता है (या शायद नहीं) की जरूरत:

  • यह दोनों नक्शे और कीवर्ड सूचियों
  • यह structs साथ structs या नक्शे मर्ज नहीं होता संभालती है ...
  • ... लेकिन आप सरल डीपमेज को लागू कर सकते हैं। आपकी पसंद के प्रकार/structs के लिए रीसोल्वर प्रोटोकॉल उन्हें गहरे विलय योग्य बनाने के लिए
  • एक गहरी_मेर/3 संस्करण जो Map.merge के समान फ़ंक्शन प्राप्त करता है/3 मामले में उदाहरण के लिए, विलय व्यवहार को संशोधित आप नहीं चाहते कीवर्ड सूचियों को मर्ज करने या आप चाहते हैं सभी सूचियों संलग्न किया जा करने के लिए
0

वहाँ Mix.Config के स्रोत में इसी तरह के कोड है: https://github.com/elixir-lang/elixir/blob/master/lib/mix/lib/mix/config.ex#L302

@doc """ 
Merges two configurations. 

The configuration of each application is merged together 
with the values in the second one having higher preference 
than the first in case of conflicts. 

## Examples 

    iex> Mix.Config.merge([app: [k: :v1]], [app: [k: :v2]]) 
    [app: [k: :v2]] 

    iex> Mix.Config.merge([app1: []], [app2: []]) 
    [app1: [], app2: []] 

""" 
def merge(config1, config2) do 
    Keyword.merge(config1, config2, fn _, app1, app2 -> 
    Keyword.merge(app1, app2, &deep_merge/3) 
    end) 
end 

defp deep_merge(_key, value1, value2) do 
    if Keyword.keyword?(value1) and Keyword.keyword?(value2) do 
    Keyword.merge(value1, value2, &deep_merge/3) 
    else 
    value2 
    end 
end 
संबंधित मुद्दे