nestdictutils#

utils.recursive_add(d, key_path, in_place=False)#

Recursively add to a dictionary from a “key path” (= sequence of keys) and the associated value.

Parameters:
ddict

Input dictionary.

key_pathtuple

The “key path” and the associated value.

in_placebool, default: False

Whether to modify the input dictionary in place or return a new dictionary.

Returns:
dict or None

Either a new dictionary (if in_place is False) or None (if in_place is True).

Examples

# Create a nested dictionary
>>> d = {1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: 10}, 7: 11}

# Add an element to the {7:8} inner dictionary and return
# a new dictionary
>>> new_d = nestdictutils.recursive_add(d, ((6, 3, 12), 13), False)
>>> new_d
{1: 2, 3: {4: 5}, 6: {3: {7: 8, 12: 13}, 7: 10}, 7: 11}
# Create a nested dictionary
>>> d = {1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: 10}, 7: 11}

# Try to add a new element associated with key 7 in the
# {7:8} inner dictionary
>>> new_d = nestdictutils.recursive_add(d, ((6, 3, 7, 12), 13), False)
You cannot add element '13' at position (6, 3, 7, 12) because
at least one key in the path is not associated with a
dictionary.
>>> new_d
{1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: 10}, 7: 11}
utils.recursive_build(key_paths)#

Recursively build a dictionary from “key paths” (= sequence of keys) and their associated values.

Parameters:
key_pathsany iterable of “key paths”

The “key paths”.

Returns:
dict

The dictionary built from the “key paths”.

Examples

# Define the key paths
>>> key_paths = (((1,2),3), ((4,),5))

# Build the dictionary
>>> d = nestdictutils.recursive_build(key_paths)
>>> d
{1: {2: 3}, 4: 5}
# Define the key paths
>>> key_paths = (((1,2),3), ((4,),5), ((4,),6))

# Build the dictionary
>>> d = nestdictutils.recursive_build(key_paths)
>>> d
{1: {2: 3}, 4: 6}
You provided several instances of the key path ((4,)).
Only ((4,), 5) will be used when building the dictionary.
utils.recursive_filter_dict(d, func, keys=None, in_place=False, permissive=False)#

Recursively filter a dictionary’s values. Only values for which the filter func returns True will be kept, while the others (together with their associated keys) will be removed from the dictionary.

Parameters:
ddict

The input dictionary.

funcany callable

Filtering callable taking as inputs the leaf values in the dictionary and returning True or False.

keyslist, set, optional

List of specific keys on whose values the filtering should be performed. This means that all values associated with keys different from those in the list will not be affected. If None, all keys and associated values will be considered.

in_placebool, default: False

Whether to modify the input dictionary in place or return a new dictionary.

permissivebool, default: False

If True and func cannot be applied to some values, return the dictionary with those values untouched.

If False, raise an error if func cannot be applied to some values.

Returns:
dict or None

Either a new dictionary (if in_place is False) or None (if in_place is True).

Examples

# Create a nested dictionary
>>> d = {1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: 10}, 7: 2}

# Define a function to filter out all values strictly
# lower than 4
f = lambda x: x > 4

# Filter the dictionary
>>> new_d = nestdictutils.map_dict(d, f, in_place = False)
>>> new_d
{3: {4: 5}, 6: {3: {7: 8}, 7: 10}}

# Note that values lower than 4 are still kept as keys,
# and only removed if they are values
# Create a nested dictionary
>>> d = {1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: [10]}, 7: 2}

# Define a function to filter out all values strictly
# lower than 4
f = lambda x: x > 4

# Filter the dictionary
>>> new_d = nestdictutils.map_dict(d, f, in_place = False)
>>> new_d
Traceback (most recent call last):
    f = lambda x: x > 4
                  ^^^^^
TypeError: '>' not supported between instances of 'list'
and 'int'

The above exception was the direct cause of the following
exception:

Traceback (most recent call last):

    new_d = nestdictutils.recursive_filter_dict(d, f, in_place = False)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    raise Exception(errstr) from e
Exception: Could not apply the function to [10].  
utils.recursive_get_key_paths(d, value)#

Get a list of “key paths” (= sequences of keys) to access a specific value inside the dictionary. If the value is present only once inside the dictionary, the output list will contain only one element.

Parameters:
ddict

Input dictionary.

valueany object apart from dict

Value whose “key paths” will be reported.

Returns:
list

List of “key paths”.

Examples

# Create a nested dictionary
>>> d = {1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: 10}, 7: 11}

# Get the "key paths" to the value 7
>>> nestdictutils.recursive_get_key_paths(d, 7)
[([6, 3], 7), ([6], 7), ([], 7)]

# Note that the last "key path" is empty because
# 7 is a key of the outer dictionary  
utils.recursive_get_values_from_key(d, key)#

Given a key, returns a list of all values in the dictionary associated with it. Since the dictionary may be nested, the same key could be found at different levels. In this case, the list will contains all values the key has been found associated with.

Parameters:
ddict

Input dictionary.

keyany immutable object

Key of interest.

Returns:
list

List of values associated with the key of interest.

Examples

# Create a nested dictionary
>>> d = {1: 2, 3: {4: 5}, 6: {3: {7: 8}}}

# Get all values associated with the key 3
>>> nestdictutils.recursive_get_values_from_key(d, 3)
[{4: 5}, {7: 8}]
utils.recursive_get_values_from_keys(d, keys)#

Given a set of keys, returns a list of all values in the dictionary associated with them (since it may be a nested dictionary, the same key could be found at different levels).

Parameters:
ddict

Input dictionary.

keysan iterable of immutable objects

Keys of interest.

Returns:
dict

Dictionary of values associated with the corresponding keys of interest.

Examples

# Create a nested dictionary
>>> d = {1: 2, 3: {4: 5}, 6: {3: {7: 8}}}

# Get all values associated with the keys 3 and 6
>>> nestdictutils.recursive_get_values_from_keys(d, [3, 6])
{3: [{4: 5}, {7: 8}], 6: [{3: {7: 8}}]}
utils.recursive_iter_key_paths(d)#

Recursively iterate over a dictionary and return a list of tuples containing the “key path” (= sequence of keys) to reach each point in the dictionary and the value that each “key path” leads to.

Parameters:
ddict

Input dictionary.

Returns:
generator

Generator of “key paths”.

Examples

# Create a nested dictionary
>>> d = {1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: 10}, 7: 11}

# Get the "key paths" for all items in the dictionary
# and list them
>>> list(nestdictutils.recursive_iter_key_paths(d))
[([1], 2), ([3, 4], 5), ([3], {4: 5}), ([6, 3, 7], 8),
([6, 3], {7: 8}), ([6, 7], 10), ([6], {3: {7: 8}, 7: 10}),
([7], 11)]
utils.recursive_map_dict(d, func, keys=None, in_place=False, permissive=False)#

Recursively traverse a dictionary mapping a function to the dictionary’s leaf values (= substituting the values which the return value of the function applied to those values).

Parameters:
ddict

The input dictionary.

funcany callable

Callable taking as inputs the leaf values of the dictionary and returning a value which will take the dictionary’s place.

keyslist, set, optional

List of specific keys on whose items the mapping should be performed. This means that all values associated with keys different from those in the list will not be affected. If None, all keys and associated values will be considered.

in_placebool, default: False

Whether to modify the input dictionary in place or return a new dictionary.

permissivebool, default: False

If True and func cannot be applied to some values, return the dictionary with those values untouched.

If False, raise an error if func cannot be applied to some values.

Returns:
dict or None

Either a new dictionary (if in_place is False) or None (if in_place is True).

Examples

# Create a nested dictionary
>>> d = {1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: 10}, 7: 11}

# Define a function to raise all values to the power of two
f = lambda x: x**2

# Raise all values to the power of two
>>> new_d = nestdictutils.map_dict(d, f, in_place = False)
>>> new_d
# Create a nested dictionary
>>> d = {1: [2], 3: {4: 5}, 6: {3: {7: 8}, 7: 10}, 7: 11}

# Define a function to raise all values to the power of two
f = lambda x: x**2

# Raise all values to the power of two
>>> new_d = nestdictutils.map_dict(d, f, in_place = False)
>>> new_d
Traceback (most recent call last):
    f = lambda x: x**2
                  ~^^~
TypeError: unsupported operand type(s) for ** or pow():
'list' and 'int'

The above exception was the direct cause of the following
exception:

Traceback (most recent call last):

    new_d = nestdictutils.map_dict(d, f, in_place = False)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    raise Exception(errstr) from e
Exception: Could not apply the function to [2].  
utils.recursive_merge_dicts(dicts)#

Merge two nested dictionaries.

Parameters:
dictsiterable of dict

Iterable of dictionaries to be merged.

Returns:
``merged_d``collections.defaultict

Merged dictionary.

Notes

If an identical key at an identical position is found in any two or more input dictionaries, the value in the dictionary that comes first in the iterable will be reported in the merged dictionary.

If the value associated to the key in two or more input dictionaries is a dictionary, the key/value pairs from all these dictionaries will be reported in the merged dictionary.

Examples

# Create a nested dictionary
>>> d = {1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: 10}, 7: 11}

# Create a second nested dictionary
>>> d2 = {14: {15: 16}, 3: 6}

# Merge the two dictionaries
>>> new_d = nestdictutils.recursive_merge_dicts([d, d2])
>>> new_d
{1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: 10}, 7: 11, 14: {15: 16}}
# Create a nested dictionary
>>> d = {1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: 10}, 7: 11}

# Create a second nested dictionary
>>> d2 = {14: {15: 16}, 3: {17: 18}}

# Create a third nested dictionary
>>> d3 = {6 : {3 : {9:10}}}

# Merge the three dictionaries
>>> new_d = nestdictutils.recursive_merge_dicts([d, d2, d3])
>>> new_d
{1: 2, 3: {4: 5, 17: 18}, 6: {3: {7: 8, 9: 10}, 7: 10},
7: 11, 14: {15: 16}}
utils.recursive_pop(d, keys, in_place=False)#

Recursively remove specific keys (and the associated values) from a dictionary.

Parameters:
ddict

Input dictionary.

keysan iterable of immutable objects

The keys to be removed.

in_placebool, default: False

Whether to modify the input dictionary in place or return a new dictionary.

Returns:
dict or None

Either a new dictionary (if in_place is False) or None (if in_place is True).

Examples

# Create a nested dictionary
>>> d = {1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: 10}, 7: 11}

# Remove keys 3 and 7 and associated values
>>> new_d = nestdictutils.recursive_pop(d, (3, 7), False)
>>> new_d
{1: 2, 6: {}}

# Note that all occurrences of the keys have been removed
# from the dictionary, not just their outermost instances      
utils.recursive_remove(d, key_path, in_place=False)#

Recursively remove a value in a dictionary given its “key path” (= sequence of keys) in the dictionary.

Parameters:
ddict

Input dictionary.

key_pathtuple

The “key path” and the associated value.

in_placebool, default: False

Whether to modify the input dictionary in place or return a new dictionary.

Returns:
dict or None

Either a new dictionary (if in_place is False) or None (if in_place is True).

Raises:
KeyError

If the “key path” is not found in the input dictionary.

ValueError

If the “key path” is found in the input dictionary, but it is associated to a value different from the one provided.

Examples

# Create a nested dictionary
>>> d = {1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: 10}, 7: 11}

# Remove element 11 in the outer dictionary
>>> new_d = nestdictutils.recursive_remove(d, ((7,), 11), False)
>>> new_d
{1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: 10}}

# Note how also the associated key has been removed from the
# dictionary
# Create a nested dictionary
>>> d = {1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: 10}, 7: 11}

# Remove element 5 in the inner dictionary {4: 5}
>>> new_d = nestdictutils.recursive_remove(d, ((3, 4), 5), False)
>>> new_d
{1: 2, 3: {}, 6: {3: {7: 8}, 7: 10}, 7: 11}

# Note how also the associated key has been removed from the
# dictionary
# Create a nested dictionary
>>> d = {1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: 10}, 7: 11}

# Try to remove element 5 given a nonexistent path (3, 6)
>>> new_d = nestdictutils.recursive_remove(d, ((3, 6), 5), False)
KeyError: 'The key path (3, 6) does not exist.'
# Create a nested dictionary
>>> d = {1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: 10}, 7: 11}

# Try to remove a nonexistent element 6 from the inner
# dictionary {4: 5}
>>> new_d = nestdictutils.recursive_remove(d, ((3, 4), 6), False)
ValueError: The key path (3, 4) was found, but its associated value
in the provided key path (6) does not correspond to the one found
in the dictionary (5).
utils.recursive_replace(d, key_path, in_place=False)#

Recursively replace a value in a dictionary from a “key path” (= sequence of keys) and the associated value.

Parameters:
ddict

Input dictionary.

key_pathtuple

The “key path” and the associated value.

in_placebool, default: False

Whether to modify the input dictionary in place or return a new dictionary.

Returns:
dict or None

Either a new dictionary (if in_place is False) or None (if in_place is True).

Examples

# Create a nested dictionary
>>> d = {1: 2, 3: {4: 5}, 6: {3: {7: 8}, 7: 10}, 7: 11}

# Replace element 5 in the {4: 5} inner dictionary
# with 4
>>> new_d = nestdictutils.recursive_replace(d, ((3, 4), 4), False)
>>> new_d
{1: 2, 3: {4: 4}, 6: {3: {7: 8}, 7: 10}, 7: 11}