Change values in dict of nested dicts using items in a list?

前端 未结 5 587
执念已碎
执念已碎 2020-11-29 10:09

How would you modify/create keys/values in a dict of nested dicts based on the values of a list, in which the last item of the list is a value for the dict, and the rest of

相关标签:
5条回答
  • 2020-11-29 10:23

    I think this works like you're after.

    def ValueModify(l, d):
      if l[0] not in d:
        d[l[0]] = dict()
      if isinstance(d[l[0]], dict):
        ValueModify(l[1:], d[l[0]])
      else:
        d[l[0]] = l[1]
    

    I'm using isinstance, which is type checking, and not generally something you do in Python, but it does set the value as expected.

    -- Edited --

    Added in the missing key check to set nested values if the original nested_dict is not fully populated.

    0 讨论(0)
  • 2020-11-29 10:25

    Here's a recursive solution.

    def unravel(d, keys):
        i = keys[0]
        keys = keys[1:]
        tmpDict = d[i]
        if type(tmpDict) != type({}):
            return tmpDict
        else:
            return unravel(tmpDict, keys)
    
    0 讨论(0)
  • 2020-11-29 10:35
    address_list = ["key1", "key1.1", "key1.2", "value"]
    
    def set_value(dict_nested, address_list):
        cur = dict_nested
        for path_item in address_list[:-2]:
            try:
                cur = cur[path_item]
            except KeyError:
                cur = cur[path_item] = {}
        cur[address_list[-2]] = address_list[-1]
    
    0 讨论(0)
  • 2020-11-29 10:38

    To insert a new key-value pair or update the value of a pair:

    import copy
    def update_nested_map(d, u, *keys):
        d = copy.deepcopy(d)
        keys = keys[0]
        if len(keys) > 1:
            d[keys[0]] = update_nested_map(d[keys[0]], u, keys[1:])
        else:
            d[keys[0]] = u
        return d
    

    test:

        >>> d = {'m': {'d': {'v': {'w': 1}}}}
    
        >>> update_nested_map(d, 999, ['m', 'd', 'v', 'w'])
        {'m': {'d': {'v': {'w': 999}}}}
    
        >>> update_nested_map(d, 999, ['m', 'd', 'v', 'z'])
        {'m': {'d': {'v': {'z': 999, 'w': 1}}}}
    
        >>> update_nested_map(d, 999, ['m', 'd', 'l'])
        {'m': {'d': {'v': {'w': 1}, 'l': 999}}}
    
        >>> update_nested_map(d, 999, ['m','d'])
        {'m': {'d': 999}}
    
    0 讨论(0)
  • 2020-11-29 10:39

    One-liner:

    keys, (newkey, newvalue) = list_address[:-2], list_address[-2:]
    reduce(dict.__getitem__, keys, dict_nested)[newkey] = newvalue
    

    Note: dict.get and operator.getitem would produce wrong exceptions here.

    An explicit for-loop as in Joel Cornett's answer might be more readable.

    If you want to create non-existing intermediate dictionaries:

    reduce(lambda d,k: d.setdefault(k, {}), keys, dict_nested)[newkey] = newvalue
    

    If you want to override existing intermediate values that are not dictionaries e.g., strings, integers:

    from collections import MutableMapping
    
    def set_value(d, keys, newkey, newvalue, default_factory=dict):
        """
        Equivalent to `reduce(dict.get, keys, d)[newkey] = newvalue`
        if all `keys` exists and corresponding values are of correct type
        """
        for key in keys:
            try:
                val = d[key]
            except KeyError:
                val = d[key] = default_factory()
            else:
                if not isinstance(val, MutableMapping):
                    val = d[key] = default_factory()
            d = val
        d[newkey] = newvalue
    

    Example

    list_address = ["key1", "key1.2", "key1.2.1", "key1.2.1.1", "value"]
    dict_nested = {
        "key1": {
                    "key1.1": {
                                "...": "...",
                    },
                    "key1.2": {
                                "key1.2.1": "change_this",
                    },
                },
    
        "key2": {
                    "...": "..."
                },
    }
    
    set_value(dict_nested, list_address[:-2], *list_address[-2:])
    assert reduce(dict.get, list_address[:-1], dict_nested) == list_address[-1]
    

    Tests

    >>> from collections import OrderedDict
    >>> d = OrderedDict()
    >>> set_value(d, [], 'a', 1, OrderedDict) # non-existent key
    >>> d.items()
    [('a', 1)]
    >>> set_value(d, 'b', 'a', 2) # non-existent intermediate key
    >>> d.items()
    [('a', 1), ('b', {'a': 2})]
    >>> set_value(d, 'a', 'b', 3) # wrong intermediate type
    >>> d.items()
    [('a', {'b': 3}), ('b', {'a': 2})]
    >>> d = {}
    >>> set_value(d, 'abc', 'd', 4)
    >>> reduce(dict.get, 'abcd', d) == d['a']['b']['c']['d'] == 4
    True
    >>> from collections import defaultdict
    >>> autovivify = lambda: defaultdict(autovivify)
    >>> d = autovivify()
    >>> set_value(d, 'abc', 'd', 4)
    >>> reduce(dict.get, 'abcd', d) == d['a']['b']['c']['d'] == 4
    True
    >>> set_value(1, 'abc', 'd', 4) #doctest:+IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    ...
    TypeError:
    >>> set_value([], 'abc', 'd', 4) #doctest:+IGNORE_EXCEPTION_DETAIL
    Traceback (most recent call last):
    ...
    TypeError:
    >>> L = [10]
    >>> set_value(L, [0], 2, 3)
    >>> L
    [{2: 3}]
    
    0 讨论(0)
提交回复
热议问题