Python | Convert flattened dictionary into nested dictionary
Last Updated :
10 Apr, 2023
Given a flattened dictionary, the task is to convert that dictionary into a nested dictionary where keys are needed to be split at ‘_’ considering where nested dictionary will be started.
Method #1: Using Naive Approach
Step-by-step approach :
- Define a function named insert that takes two parameters, a dictionary (dct) and a list (lst). This function iterates over all elements of the input list except the last two elements, and creates nested dictionaries corresponding to each element in the list.
- The insert() function then updates the dictionary (dct) with a key-value pair where the key is the second last element of the input list, and the value is the last element of the input list.
- Define a function named convert_nested that takes a dictionary (dct) as an input. This function first initializes an empty dictionary named result to store the output nested dictionary.
- The function then creates an iterator named lists, which is a list of lists where each list represents a hierarchical flow of keys and values from the input dictionary. To create this iterator, it splits each key of the input dictionary by the ‘_’ character and appends the corresponding value to the list.
- The convert_nested() function then iterates over each list in the lists iterator and inserts it into the result dictionary using the insert() function.
- Finally, the convert_nested() function returns the resulting nested dictionary result.
- Define an initial dictionary named ini_dict containing key-value pairs.
- Print the initial dictionary using the print() function.
- Create a new list named _split_dict that contains lists representing the hierarchical flow of keys and values from the initial dictionary by splitting each key of the dictionary by the ‘_’ character and appending the corresponding value to the list.
- Print the final nested dictionary by calling the convert_nested() function on the initial dictionary and print the resulting nested dictionary using the print() function.
Below is the implementation of the above approach:
Python3
def insert(dct, lst):
for x in lst[: - 2 ]:
dct[x] = dct = dct.get(x, dict ())
dct.update({lst[ - 2 ]: lst[ - 1 ]})
def convert_nested(dct):
result = dict ()
lists = ([ * k.split( "_" ), v] for k, v in dct.items())
for lst in lists:
insert(result, lst)
return result
ini_dict = { 'Geeks_for_for' : 1 , 'Geeks_for_geeks' : 4 ,
'for_geeks_Geeks' : 3 , 'geeks_Geeks_for' : 7 }
print ( "initial_dictionary" , str (ini_dict))
_split_dict = [[ * a.split( '_' ), b] for a, b in ini_dict.items()]
print ( "final_dictionary" , str (convert_nested(ini_dict)))
|
Output
initial_dictionary {'Geeks_for_for': 1, 'Geeks_for_geeks': 4, 'for_geeks_Geeks': 3, 'geeks_Geeks_for': 7}
final_dictionary {'Geeks': {'for': {'for': 1, 'geeks': 4}}, 'for': {'geeks': {'Geeks': 3}}, 'geeks': {'Geeks': {'for': 7}}}
Time Complexity: O(n)
Auxiliary Space: O(n)
Method #2: Using default dict and recursive approach
Python3
def nest_dict(dict1):
result = {}
for k, v in dict1.items():
split_rec(k, v, result)
return result
def split_rec(k, v, out):
k, * rest = k.split( '_' , 1 )
if rest:
split_rec(rest[ 0 ], v, out.setdefault(k, {}))
else :
out[k] = v
ini_dict = { 'Geeks_for_for' : 1 , 'Geeks_for_geeks' : 4 ,
'for_geeks_Geeks' : 3 , 'geeks_Geeks_for' : 7 }
print ( "initial_dictionary" , str (ini_dict))
print ( "final_dictionary" , str (nest_dict(ini_dict)))
|
Output
initial_dictionary {'Geeks_for_for': 1, 'Geeks_for_geeks': 4, 'for_geeks_Geeks': 3, 'geeks_Geeks_for': 7}
final_dictionary {'Geeks': {'for': {'for': 1, 'geeks': 4}}, 'for': {'geeks': {'Geeks': 3}}, 'geeks': {'Geeks': {'for': 7}}}
Time complexity: O(n), where n is the length of the key string.
Auxiliary space: O(N * n), where N is the number of items in the input dictionary and n is the length of the longest key string.
Method #3: Using reduce and getitem
Python3
from collections import defaultdict
from functools import reduce
from operator import getitem
def getFromDict(dataDict, mapList):
return reduce (getitem, mapList, dataDict)
tree = lambda : defaultdict(tree)
d = tree()
def default_to_regular(d):
if isinstance (d, defaultdict):
d = {k: default_to_regular(v) for k, v in d.items()}
return d
ini_dict = { 'Geeks_for_for' : 1 , 'Geeks_for_geeks' : 4 ,
'for_geeks_Geeks' : 3 , 'geeks_Geeks_for' : 7 }
print ( "initial_dictionary" , str (ini_dict))
for k, v in ini_dict.items():
* keys, final_key = k.split( '_' )
getFromDict(d, keys)[final_key] = v
print ( "final_dictionary" , str (default_to_regular(d)))
|
Output
initial_dictionary {'Geeks_for_for': 1, 'Geeks_for_geeks': 4, 'for_geeks_Geeks': 3, 'geeks_Geeks_for': 7}
final_dictionary {'Geeks': {'for': {'for': 1, 'geeks': 4}}, 'for': {'geeks': {'Geeks': 3}}, 'geeks': {'Geeks': {'for': 7}}}
Time complexity: O(n), where n is the number of key-value pairs in the dictionary.
Auxiliary space: O(nm), where n and m are the same as explained above
Method 4 : Using a recursive function without defaultdict or reduce
uses a recursive function add_to_nested_dict to add values to a nested dictionary. The function takes a nested dictionary, a list of keys, and a value as inputs. If the list of keys has only one key, the function adds the value to the nested dictionary at that key. Otherwise, it creates a nested dictionary at the first key if it doesn’t already exist, and then recursively calls the function with the remaining keys and value.
step-by-step approach :
- Create a function add_to_nested_dict that takes three arguments: a nested dictionary, a list of keys, and a value. This function will add the value to the nested dictionary at the specified keys.
- Check if the length of the keys list is 1. If it is, add the value to the nested dictionary at the last key in the list.
- If the length of the keys list is greater than 1, check if the first key is in the nested dictionary. If it isn’t, create a new nested dictionary at that key.
- Recursively call the add_to_nested_dict function with the nested dictionary at the first key in the list, the remaining keys in the list, and the value.
- Create an empty dictionary d that will hold the nested dictionary.
- Iterate over the flattened dictionary ini_dict using a for loop.
- Split each key in ini_dict into a list of keys using the split method.
- Call the add_to_nested_dict function with the empty dictionary d, the list of keys, and the value.
- Print the final nested dictionary d.
Python3
def add_to_nested_dict(nested_dict, keys, value):
if len (keys) = = 1 :
nested_dict[keys[ 0 ]] = value
else :
if keys[ 0 ] not in nested_dict:
nested_dict[keys[ 0 ]] = {}
add_to_nested_dict(nested_dict[keys[ 0 ]], keys[ 1 :], value)
ini_dict = { 'Geeks_for_for' : 1 , 'Geeks_for_geeks' : 4 ,
'for_geeks_Geeks' : 3 , 'geeks_Geeks_for' : 7 }
print ( "initial_dictionary" , str (ini_dict))
d = {}
for k, v in ini_dict.items():
keys = k.split( '_' )
add_to_nested_dict(d, keys, v)
print ( "final_dictionary" , str (d))
|
Output
initial_dictionary {'Geeks_for_for': 1, 'Geeks_for_geeks': 4, 'for_geeks_Geeks': 3, 'geeks_Geeks_for': 7}
final_dictionary {'Geeks': {'for': {'for': 1, 'geeks': 4}}, 'for': {'geeks': {'Geeks': 3}}, 'geeks': {'Geeks': {'for': 7}}}
The time complexity of this approach is O(nm), where n is the number of keys in the flattened dictionary and m is the maximum depth of the nested dictionary.
The auxiliary space is also O(nm), since the function creates nested dictionaries as needed.
Method 5: Using a loop to iterate through the list of keys
- Create an empty dictionary.
- Iterate through the items in the initial dictionary using a for loop.
- Split the keys into a list using the split() method and store the value in a variable.
- Create a variable temp that references the empty dictionary.
- Iterate through the list of keys using a for loop.
- If the key exists in temp, update temp to reference the value of that key.
- If the key does not exist in temp, create a new key with an empty dictionary as its value and update temp to reference that value.
- When the loop through the keys is finished, set the final value of the last key in the list to be the value from the initial dictionary.
- Return the nested dictionary.
Python3
def add_to_nested_dict(nested_dict, keys, value):
temp = nested_dict
for key in keys[: - 1 ]:
temp = temp.setdefault(key, {})
temp[keys[ - 1 ]] = value
return nested_dict
ini_dict = { 'Geeks_for_for' : 1 , 'Geeks_for_geeks' : 4 ,
'for_geeks_Geeks' : 3 , 'geeks_Geeks_for' : 7 }
print ( "initial_dictionary" , str (ini_dict))
d = {}
for k, v in ini_dict.items():
keys = k.split( '_' )
add_to_nested_dict(d, keys, v)
print ( "final_dictionary" , str (d))
|
Output
initial_dictionary {'Geeks_for_for': 1, 'Geeks_for_geeks': 4, 'for_geeks_Geeks': 3, 'geeks_Geeks_for': 7}
final_dictionary {'Geeks': {'for': {'for': 1, 'geeks': 4}}, 'for': {'geeks': {'Geeks': 3}}, 'geeks': {'Geeks': {'for': 7}}}
Time complexity: O(n*m), where n is the number of items in the initial dictionary and m is the maximum number of keys in a single item.
Auxiliary space: O(m), where m is the maximum number of keys in a single item.
Please Login to comment...