Merge Dictionaries Preserving Old Keys and New Values
<p>I'm writing a Python script that parses RSS feeds. I want to maintain a dictionary of entries from the feed that gets updated periodically. Entries that no longer exist in the feed should be removed, new entries should get a default value, and the values for previously seen entries should remain unchanged.</p>

<p>This is best explained by example, I think:</p>

<pre><code>&gt;&gt;&gt; old = {
...     'a': 1,
...     'b': 2,
...     'c': 3
... }
&gt;&gt;&gt; new = {
...     'c': 'x',
...     'd': 'y',
...     'e': 'z'
... }
&gt;&gt;&gt; out = some_function(old, new)
&gt;&gt;&gt; out
{'c': 3, 'd': 'y', 'e': 'z'}
</code></pre>

<p>Here's my current attempt at this:</p>

<pre><code>def merge_preserving_old_values_and_new_keys(old, new):
       out = {}
       for k, v in new.items():
           out[k] = v
       for k, v in old.items():
           if k in out:
               out[k] = v
       return out
</code></pre>

<p>This works, but it seems to me there might be a better or more clever way.</p>

<p>EDIT: If you feel like testing your function:</p>

<pre><code>def my_merge(old, new):
    pass

old = {'a': 1, 'b': 2, 'c': 3}
new = {'c': 'x', 'd': 'y', 'e': 'z'}

out = my_merge(old, new)
assert out == {'c': 3, 'd': 'y', 'e': 'z'}
</code></pre>

<p>EDIT 2:
Defining Martijn Pieters' answer as <code>set_merge</code>, bravosierra99's as <code>loop_merge</code>, and my first attempt as <code>orig_merge</code>, I get the following timing results:</p>

<pre><code>&gt;&gt;&gt; setup="""
... old = {'a': 1, 'b': 2, 'c': 3}
... new = {'c': 'x', 'd': 'y', 'e': 'z'}
... from __main__ import set_merge, loop_merge, orig_merge
... """
&gt;&gt;&gt; timeit.timeit('set_merge(old, new)', setup=setup)
3.4415210600000137
&gt;&gt;&gt; timeit.timeit('loop_merge(old, new)', setup=setup)
1.161155690000669
&gt;&gt;&gt; timeit.timeit('orig_merge(old, new)', setup=setup)
1.1776735319999716
</code></pre>

<p>I find this surprising, since I didn't expect the dictionary view approach to be that much slower.</p>

<p>I'm not 100% the best way to add this information to the discussion: feel free to edit/redistribute it if necessary.</p>

<p>Here are timing results for all of the methods discussed here.</p>

<pre><code>from timeit import timeit

def loop_merge(old, new):
    out = {}
    for k, v in new.items():
        if k in old:
            out[k] = old[k]
        else:
                out[k] = v
    return out

def set_merge(old, new):
    out = new.copy()
    out.update((k, old[k]) for k in old.keys() &amp; new.keys())
    return out

def comp_merge(old, new):
    return {k: old[k] if k in old else v for k, v in new.items()}

def orig_merge(old, new):
    out = {}
    for k, v in new.items():
        out[k] = v
    for k, v in old.items():
        if k in out:
            out[k] = v
    return out


old = {'a': 1, 'b': 2, 'c': 3}
new = {'c': 'x', 'd': 'y', 'e': 'z'}
out = {'c': 3, 'd': 'y', 'e': 'z'}

assert loop_merge(old, new) == out
assert set_merge(old, new) == out
assert comp_merge(old, new) == out
assert orig_merge(old, new) == out

setup = """
from __main__ import old, new, loop_merge, set_merge, comp_merge, orig_merge
"""

for a in ['loop', 'set', 'comp', 'orig']:
    time = timeit('{}_merge(old, new)'.format(a), setup=setup)
    print('{}: {}'.format(a, time))

size = 10**4
large_old = {i: 'old' for i in range(size)}
large_new = {i: 'new' for i in range(size//2, size)}

setup = """
from __main__ import large_old, large_new, loop_merge, set_merge, comp_merge, orig_merge
"""

for a in ['loop', 'set', 'comp', 'orig']:
    time = timeit('{}_merge(large_old, large_new)'.format(a), setup=setup)
    print('{}: {}'.format(a, time))
</code></pre>

<p>The winner is the improved looping method!</p>

<pre><code>$ python3 merge.py
loop: 0.7791572390015062  # small dictionaries
set: 3.1920828100010112
comp: 1.1180207730030816
orig: 1.1681104259987478
loop: 927.2149353210007  # large dictionaries
set: 1696.8342713210004
comp: 902.039078668
orig: 1373.0389542560006
</code></pre>

<p>I'm disappointed, because the dictionary view/set operation method is much cooler.</p>

<p>With larger dictionaries (10^4 items), the dictionary comprehension method pulls ahead of the improved looping method and far ahead of the original method. The set operation method still performs the slowest.</p>

python dictionary