Merge two dictionaries and keep the values for duplicate keys in Python

前端 未结 8 2312
广开言路
广开言路 2021-02-08 19:16

Let\'s suppose that I have two dictionaries:

dic1 =  { \"first\":1, \"second\":4, \"third\":8} 
dic2 =  { \"first\":9, \"second\":5, \"fourth\":3}
相关标签:
8条回答
  • 2021-02-08 19:44

    Using set and dictionary comprehension

    L = [d1, d2]
    dups = set(d1.keys() & d2.keys())
    d = {k: [L[0][k], L[1][k]] if k in dups else i[k] for i in L for k in i}
    
    {'first': [1, 9], 'second': [4, 5], 'third': 8, 'fourth': 3}
    
    0 讨论(0)
  • 2021-02-08 19:45
    from copy import deepcopy
    
    
    def _add_value_to_list(value, lis):
        if value:
            if isinstance(value, list):
                lis.extend(value)
            else:
                lis.append(value)
        else:
            pass
    
    
    def _merge_value(value_a, value_b):
        merged_value = []
        _add_value_to_list(value_a, merged_value)
        _add_value_to_list(value_b, merged_value)
        return merged_value
    
    
    def _recursion_merge_dict(new_dic, dic_a, dic_b):
        if not dic_a or not dic_b:
            return new_dic
        else:
            if isinstance(new_dic, dict):
                for k, v in new_dic.items():
                    new_dic[k] = _recursion_merge_dict(v, dic_a.get(k, {}), dic_b.get(k, {}))
                return new_dic
            else:
                return _merge_value(dic_a, dic_b)
    
    
    def merge_dicts(dic_a, dic_b):
        new_dic = deepcopy(dic_a)
        new_dic.update(dic_b)
    
        return _recursion_merge_dict(new_dic, dic_a, dic_b)
    
    0 讨论(0)
  • 2021-02-08 19:47

    You can use a defaultdict to hold lists, and then just append the values to them. This approach easily extends to an arbitrary number of dictionaries.

    from collections import defaultdict
    
    dd = defaultdict(list)
    
    dics = [dic1, dic2]
    for dic in dics:
        for key, val in dic.iteritems():  # .items() in Python 3.
            dd[key].append(val)
    
    >>> dict(dd)
    {'first': [1, 9], 'fourth': [3], 'second': [4, 5], 'third': [8]}
    

    All of the keys with a single value are still held within a list, which is probably the best way to go. You could, however, change anything of length one into the actual value, e.g.

    for key, val in dd.iteritems():  # .items() in Python 3.
        if len(val) == 1
            dd[key] = val[0]
    
    0 讨论(0)
  • 2021-02-08 19:54

    In general, I would say it's bad practice to cast the values of different keys as different object types. I would simply do something like:

    def merge_values(val1, val2):
        if val1 is None:
            return [val2]
        elif val2 is None:
            return [val1]
        else:
            return [val1, val2]
    dict3 = {
        key: merge_values(dic1.get(key), dic2.get(key))
        for key in set(dic1).union(dic2)
    }
    
    0 讨论(0)
  • 2021-02-08 19:57

    Here's a naive solution; copy one of the dictionaries over to the result and iterate over the other dictionary's keys and values, adding lists to the result as necessary. Since there are only two dictionaries, no merged list will have more than 2 items.

    dic1 = {"first": 1, "second": 4, "third": 8} 
    dic2 = {"first": 9, "second": 5, "fourth": 3}
    dic3 = dict(dic2)
    
    for k, v in dic1.items():
        dic3[k] = [dic3[k], v] if k in dic3 else v
    
    print(dic3) # => {'first': [9, 1], 'second': [5, 4], 'fourth': 3, 'third': 8}
    

    If you'd like single values to be lists (likely better design; mixed types aren't much fun to deal with) you can use:

    dic3 = {k: [v] for k, v in dic2.items()}
    
    for k, v in dic1.items():
        dic3[k] = dic3[k] + [v] if k in dic3 else [v]
    
    print(dic3) # => {'first': [9, 1], 'second': [5, 4], 'fourth': [3], 'third': [8]}
    

    Generalizing it to any number of dictionaries:

    def merge_dicts(*dicts):
        """
        >>> merge_dicts({"a": 2}, {"b": 4, "a": 3}, {"a": 1})
        {'a': [2, 3, 1], 'b': [4]}
        """
        merged = {}
        
        for d in dicts:
            for k, v in d.items():
                if k not in merged:
                    merged[k] = []
    
                merged[k].append(v)
        
        return merged
    

    You can use collections.defaultdict to clean it up a bit if you don't mind the import:

    from collections import defaultdict
    
    def merge_dicts(*dicts):
        """
        >>> merge_dicts({"a": 2}, {"b": 4, "a": 3}, {"a": 1})
        defaultdict(<class 'list'>, {'a': [2, 3, 1], 'b': [4]})
        """
        merged = defaultdict(list)
        
        for d in dicts:
            for k, v in d.items():
                merged[k].append(v)
        
        return merged
    
    0 讨论(0)
  • 2021-02-08 19:58

    Solution for dict of lists (adapted from @dawg):

    dic1 =  { "first":[1], "second":[4], "third":[8]} 
    dic2 =  { "first":[9], "second":[5], "fourth":[3]}
    dic_new={}
    for k,v in list(dic1.items())+list(dic2.items()):
        dic_new.setdefault(k, []).extend(v)
    >>> dic_new
    {'first': [1, 9], 'second': [4, 5], 'third': [8], 'fourth': [3]}
    
    0 讨论(0)
提交回复
热议问题