Given a nested dictionary, the task is to convert this dictionary into a flattened dictionary where the key is separated by ‘_’ in case of the nested key to be started.
Method #1: Using Naive Approach
Step-by-step approach :
- The function checks if the input dd is a dictionary. If it is, then it iterates over each key-value pair in the dictionary, and calls the flatten_dict function recursively on the value of each key. It concatenates the original key and the returned key from the recursive call with the separator, and uses that as the new key in the flattened dictionary. It sets the value of the new key to the returned value from the recursive call.
- If the input dd is not a dictionary, the function creates a new dictionary with a single key-value pair, where the key is the prefix argument (which is the path to the current value in the original nested dictionary), and the value is the input dd itself.
- The code then initializes a nested dictionary ini_dict with some sample data.
- The code prints the initial dictionary ini_dict.
- The code calls the flatten_dict function on the ini_dict dictionary and prints the resulting flattened dictionary.
Python3
# Python code to demonstrate # conversion of nested dictionary # into flattened dictionary # code to convert ini_dict to flattened dictionary # default separator '_' def flatten_dict(dd, separator = '_' , prefix = ''): return { prefix + separator + k if prefix else k : v for kk, vv in dd.items() for k, v in flatten_dict(vv, separator, kk).items() } if isinstance (dd, dict ) else { prefix : dd } # initialising_dictionary ini_dict = { 'Lazyroar' : { 'Geeks' : { 'for' : 7 }}, 'for' : { 'Lazyroar' : { 'Geeks' : 3 }}, 'Geeks' : { 'for' : { 'for' : 1 , 'Lazyroar' : 4 }}} # printing initial dictionary print ( "initial_dictionary" , str (ini_dict)) # printing final dictionary print ( "final_dictionary" , str (flatten_dict(ini_dict))) |
initial_dictionary {‘Lazyroar’: {‘Geeks’: {‘for’: 7}}, ‘Geeks’: {‘for’: {‘Lazyroar’: 4, ‘for’: 1}}, ‘for’: {‘Lazyroar’: {‘Geeks’: 3}}}
final_dictionary {‘Geeks_for_for’: 1, ‘Lazyroar_Geeks_for’: 7, ‘for_Lazyroar_Geeks’: 3, ‘Geeks_for_Lazyroar’: 4}
Time complexity: O(n^2), where n is the total number of keys in the nested dictionary.
Auxiliary space: O(n), where n is the total number of keys in the nested dictionary. The auxiliary space is used to store the result of the flatten dictionary in the form of a new dictionary.
Method #2: Using mutableMapping
Python3
# Python code to demonstrate # conversion of nested dictionary # into flattened dictionary from collections import MutableMapping # code to convert ini_dict to flattened dictionary # default separator '_' def convert_flatten(d, parent_key = ' ', sep =' _'): items = [] for k, v in d.items(): new_key = parent_key + sep + k if parent_key else k if isinstance (v, MutableMapping): items.extend(convert_flatten(v, new_key, sep = sep).items()) else : items.append((new_key, v)) return dict (items) # initialising_dictionary ini_dict = { 'Lazyroar' : { 'Geeks' : { 'for' : 7 }}, 'for' : { 'Lazyroar' : { 'Geeks' : 3 }}, 'Geeks' : { 'for' : { 'for' : 1 , 'Lazyroar' : 4 }}} # printing initial dictionary print ( "initial_dictionary" , str (ini_dict)) # printing final dictionary print ( "final_dictionary" , str (convert_flatten(ini_dict))) |
initial_dictionary {‘Geeks’: {‘for’: {‘for’: 1, ‘Lazyroar’: 4}}, ‘for’: {‘Lazyroar’: {‘Geeks’: 3}}, ‘Lazyroar’: {‘Geeks’: {‘for’: 7}}}
final_dictionary {‘Geeks_for_Lazyroar’: 4, ‘for_Lazyroar_Geeks’: 3, ‘Lazyroar_Geeks_for’: 7, ‘Geeks_for_for’: 1}
Time Complexity: O(n)
Auxiliary Space: O(n)
Method #3: Using Python Generators
Python3
# Python code to demonstrate # conversion of nested dictionary # into flattened dictionary my_map = { "a" : 1 , "b" : { "c" : 2 , "d" : 3 , "e" : { "f" : 4 , 6 : "a" , 5 :{ "g" : 6 }, "l" :[ 1 , "two" ] } }} # Expected Output # {'a': 1, 'b_c': 2, 'b_d': 3, 'b_e_f': 4, 'b_e_6': 'a', 'b_e_5_g': 6, 'b_e_l': [1, 'two']} ini_dict = { 'Lazyroar' : { 'Geeks' : { 'for' : 7 }}, 'for' : { 'Lazyroar' : { 'Geeks' : 3 }}, 'Geeks' : { 'for' : { 'for' : 1 , 'Lazyroar' : 4 }}} # Expected Output # {‘Geeks_for_Lazyroar’: 4, ‘for_Lazyroar_Geeks’: 3, ‘Geeks_for_for’: 1, ‘Lazyroar_Geeks_for’: 7} def flatten_dict(pyobj, keystring = ''): if type (pyobj) = = dict : keystring = keystring + '_' if keystring else keystring for k in pyobj: yield from flatten_dict(pyobj[k], keystring + str (k)) else : yield keystring, pyobj print ( "Input : %s\nOutput : %s\n\n" % (my_map, { k:v for k,v in flatten_dict(my_map) })) print ( "Input : %s\nOutput : %s\n\n" % (ini_dict, { k:v for k,v in flatten_dict(ini_dict) })) |
initial_dictionary {‘for’: {‘Lazyroar’: {‘Geeks’: 3}}, ‘Lazyroar’: {‘Geeks’: {‘for’: 7}}, ‘Geeks’: {‘for’: {‘for’: 1, ‘Lazyroar’: 4}}}
final_dictionary {‘Geeks_for_Lazyroar’: 4, ‘for_Lazyroar_Geeks’: 3, ‘Geeks_for_for’: 1, ‘Lazyroar_Geeks_for’: 7}
Time complexity: O(n), where n is the total number of keys in the nested dictionary
Auxiliary space: O(n), where n is the total number of keys in the nested dictionary.
Method #4: Using recursion and a separator
- Define a function “flatten_dict” that takes in three parameters: dd (a dictionary), separator (a string, default value “_”), and prefix (a string, default value “”).
- Initialize an empty dictionary “res”.
- Iterate through each key-value pair in the dictionary “dd”.
- Check if the value associated with the current key is itself a dictionary or not. If it is a dictionary, then recursively call the “flatten_dict” function with the value as the new dictionary to be flattened, the separator and prefix updated with the current key and separator value.
- If the value is not a dictionary, then add the key-value pair to the “res” dictionary, where the key is the concatenation of prefix, separator and current key.
- Return the final “res” dictionary.
Python3
# code to convert ini_dict to flattened dictionary def flatten_dict(dd, separator = '_' , prefix = ''): res = {} for key, value in dd.items(): if isinstance (value, dict ): res.update(flatten_dict(value, separator, prefix + key + separator)) else : res[prefix + key] = value return res # initialising_dictionary ini_dict = { 'Lazyroar' : { 'Geeks' : { 'for' : 7 }}, 'for' : { 'Lazyroar' : { 'Geeks' : 3 }}, 'Geeks' : { 'for' : { 'for' : 1 , 'Lazyroar' : 4 }}} # printing initial dictionary print ( "initial dictionary" , str (ini_dict)) # flattening the dictionary res = flatten_dict(ini_dict) # printing final dictionary print ( "final dictionary" , str (res)) #This code is contributed by Vinay Pinjala. |
initial dictionary {'Lazyroar': {'Geeks': {'for': 7}}, 'for': {'Lazyroar': {'Geeks': 3}}, 'Geeks': {'for': {'for': 1, 'Lazyroar': 4}}} final dictionary {'Lazyroar_Geeks_for': 7, 'for_Lazyroar_Geeks': 3, 'Geeks_for_for': 1, 'Geeks_for_Lazyroar': 4}
The time complexity of this algorithm is O(N), where N is the total number of keys in the nested dictionary. This is because the algorithm iterates through each key exactly once.
The space complexity is also O(N), where N is the total number of keys, since the algorithm creates a new dictionary “res” to store the flattened dictionary.
Method 5: Using a stack data structure
The idea is to iterate through the dictionary and push all nested dictionaries into a stack. Then, we can pop each dictionary from the stack, iterate through its key-value pairs, and append the key-value pairs to a flattened dictionary.
- Initialize an empty stack.
- Push the initial dictionary onto the stack.
- Initialize an empty flattened dictionary.
- While the stack is not empty, do the following:
a. Pop the top dictionary from the stack.
b. For each key-value pair in the popped dictionary, do the following:
i. If the value is a dictionary, push it onto the stack with its key as the prefix.
ii. Otherwise, add the key-value pair to the flattened dictionary with the prefix and separator (default is ‘_’). - Return the flattened dictionary.
Python3
# code to convert ini_dict to flattened dictionary using stack data structure def flatten_dict(dd, separator = '_' , prefix = ''): stack = [(dd, prefix)] flat_dict = {} while stack: cur_dict, cur_prefix = stack.pop() for key, val in cur_dict.items(): new_key = cur_prefix + separator + key if cur_prefix else key if isinstance (val, dict ): stack.append((val, new_key)) else : flat_dict[new_key] = val return flat_dict # initialising_dictionary ini_dict = { 'Lazyroar' : { 'Geeks' : { 'for' : 7 }}, 'for' : { 'Lazyroar' : { 'Geeks' : 3 }}, 'Geeks' : { 'for' : { 'for' : 1 , 'Lazyroar' : 4 }}} # printing initial dictionary print ( "initial_dictionary" , str (ini_dict)) # printing final dictionary print ( "final_dictionary" , str (flatten_dict(ini_dict))) |
initial_dictionary {'Lazyroar': {'Geeks': {'for': 7}}, 'for': {'Lazyroar': {'Geeks': 3}}, 'Geeks': {'for': {'for': 1, 'Lazyroar': 4}}} final_dictionary {'Geeks_for_for': 1, 'Geeks_for_Lazyroar': 4, 'for_Lazyroar_Geeks': 3, 'Lazyroar_Geeks_for': 7}
Time complexity: O(n), where n is the total number of key-value pairs in the input nested dictionary.
Auxiliary space: O(n), where n is the total number of key-value pairs in the input nested dictionary.
Method 6: Using a recursive function without a separator
Here is an alternative method that uses a recursive function without a separator. It works by iterating over the items of the dictionary and checking whether each value is another dictionary. If it is, the function calls itself recursively and concatenates the key with the new keys returned by the recursive call. If it is not, the function simply adds the key-value pair to the result dictionary.
Here’s the step by step approach:
- Define a function called flatten_dict_recursive that takes a dictionary d as input.
- Initialize an empty dictionary called result.
- For each key-value pair in d, do the following:
a. If the value is a dictionary, call flatten_dict_recursive with the value and concatenate the returned keys with the current key.
b. If the value is not a dictionary, add the current key and value to result. - Return result.
Python3
def flatten_dict(dd, separator = '_' , prefix = ''): stack = [(dd, prefix)] flat_dict = {} while stack: cur_dict, cur_prefix = stack.pop() for key, val in cur_dict.items(): new_key = cur_prefix + separator + key if cur_prefix else key if isinstance (val, dict ): stack.append((val, new_key)) else : flat_dict[new_key] = val return flat_dict # initialising_dictionary ini_dict = { 'Lazyroar' : { 'Geeks' : { 'for' : 7 }}, 'for' : { 'Lazyroar' : { 'Geeks' : 3 }}, 'Geeks' : { 'for' : { 'for' : 1 , 'Lazyroar' : 4 }}} # printing initial dictionary print ( "initial_dictionary" , str (ini_dict)) # printing final dictionary print ( "final_dictionary" , str (flatten_dict(ini_dict, separator = ''))) |
initial_dictionary {'Lazyroar': {'Geeks': {'for': 7}}, 'for': {'Lazyroar': {'Geeks': 3}}, 'Geeks': {'for': {'for': 1, 'Lazyroar': 4}}} final_dictionary {'Geeksforfor': 1, 'GeeksforLazyroar': 4, 'forLazyroarGeeks': 3, 'LazyroarGeeksfor': 7}
Time complexity: The time complexity of this function is O(n), where n is the total number of key-value pairs in the dictionary.
Auxiliary space: The auxiliary space complexity of this function is O(n), where n is the total number of key-value pairs in the dictionary.