DEAR PEOPLE FROM THE FUTURE: Here's what we've figured out so far...

Welcome! This is a Q&A website for computer programmers and users alike, focused on helping fellow programmers and users. Read more

What are you stuck on? Ask a question and hopefully somebody will be able to help you out!
+3 votes

Is this the correct way of adding the values of two dictionaries? It seems to me that the test for an empty dictionary is superfluous but I'm not sure.

mymodule.py

def merge(dict_1: Union[Dict[str, int], None], dict_2: Union[Dict[str, int], None]) -> dict:
    """
    Merge 2 dicts of ints: keys from both dicts, values are the sum of values (one dict can be None).
    :dict_1: {"a": 1, "b": 2}
    :dict_2: {"a": 3, "c": 3} or None
    :return: {"a": 4, "b": 2, "c": 3} or {"a": 1, "b": 2}
    """
    if dict_2 is None:
        return dict_1
    elif dict_1 is None:
        return dict_2
    result = dict()

    for key in dict_1.keys() | dict_2.keys():  # union of all keys
        result[key] = dict_1.get(key, 0) + dict_2.get(key, 0)  # sum quantities or 0 if key not found

    return result

mymodule_test.py

import unittest

import mymodule

class TestMerge(unittest.TestCase):
    def test_merge(self) -> None:
        dict_1 = {"a": 1, "b": 2}
        dict_2 = {"a": 3, "c": 3}
        expected = {"a": 4, "b": 2, "c": 3} or {"a": 1, "b": 2}
        actual = bestdeck.merge(dict_1, dict_2)
        assert expected == actual
    def test_empty_merge(self) -> None:
        dict_1 = {"a": 1, "b": 2}
        dict_2 = None
        expected = dict_1
        actual = bestdeck.merge(dict_1, dict_2)
        assert expected == actual

if __name__ == '__main__':
    unittest.main()
by
edited by

1 Answer

+1 vote
 
Best answer

Yes if you expect NoneType as input, it's perfectly fine to check the types

def merge(d1, d2):
    if d1 is None and d2 is None: return None
    if d1 is None: return d2
    if d2 is None: return d1
    
    return { key: d1.get(key, 0) + d2.get(key, 0) for key in d1.keys() | d2.keys() }

But if NoneType is expected to be an edge case, an "exception to the rule" rather than a regular input, you could replace if..else with try..except blocks

def merge(d1, d2):
    try:
        result = dict(d1)
    except TypeError:
        result = {}

    try:
        for key, value in d2.items():
            result[key] = result.get(key, 0) + value
    except AttributeError:
            pass
    
    return result

They are different styles (LBYL vs EAFP) and you can use which one is more convenient.

Although for your particular problem that you are describing, there is already a specialized subclass called collections.Counter:

from collections import Counter
def merge(d1, d2):
    result = Counter(d1)
    result.update(d2)
    return dict(result)
by
selected by
Contributions licensed under CC0
...