Open In App

Python | Convert flattened dictionary into nested dictionary

Improve
Improve
Like Article
Like
Save
Share
Report

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 :

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. The convert_nested() function then iterates over each list in the lists iterator and inserts it into the result dictionary using the insert() function.
  6. Finally, the convert_nested() function returns the resulting nested dictionary result.
  7. Define an initial dictionary named ini_dict containing key-value pairs.
  8. Print the initial dictionary using the print() function.
  9. 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.
  10. 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




# Python code to demonstrate
# conversion of flattened dictionary
# into nested dictionary
 
 
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):
    # empty dict to store the result
    result = dict()
 
    # create an iterator of lists
    # representing nested or hierarchical flow
    lists = ([*k.split("_"), v] for k, v in dct.items())
 
    # insert each list into the result
    for lst in lists:
        insert(result, lst)
    return result
         
# initialising_dictionary
ini_dict = {'Geeks_for_for':1,'Geeks_for_geeks':4,
            'for_geeks_Geeks':3,'geeks_Geeks_for':7}
 
# printing initial dictionary
print ("initial_dictionary", str(ini_dict))
 
# code to convert ini_dict to nested
# dictionary splitting_dict_keys
_split_dict = [[*a.split('_'), b] for a, b in ini_dict.items()]
 
 
# printing final dictionary
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




# Python code to demonstrate
# conversion of flattened dictionary
# into nested dictionary
 
# code to convert dict into nested dict
def nest_dict(dict1):
    result = {}
    for k, v in dict1.items():
         
        # for each key call method split_rec which
        # will split keys to form recursively
        # nested dictionary
        split_rec(k, v, result)
    return result
 
def split_rec(k, v, out):
     
    # splitting keys in dict
    # calling_recursively to break items on '_'
    k, *rest = k.split('_', 1)
    if rest:
        split_rec(rest[0], v, out.setdefault(k, {}))
    else:
        out[k] = v
 
         
# initialising_dictionary
ini_dict = {'Geeks_for_for':1,'Geeks_for_geeks':4,
            'for_geeks_Geeks':3,'geeks_Geeks_for':7}
 
# printing initial dictionary
print ("initial_dictionary", str(ini_dict))
 
# printing final dictionary
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




# Python code to demonstrate
# conversion of flattened dictionary
# into nested dictionary
 
from collections import defaultdict
from functools import reduce
from operator import getitem
 
 
def getFromDict(dataDict, mapList):
     
    # Iterate nested dictionary
    return reduce(getitem, mapList, dataDict)
     
# instantiate nested defaultdict of defaultdicts
tree = lambda: defaultdict(tree)
d = tree()
 
# converting default_dict_to regular dict
def default_to_regular(d):
     
    """Convert nested defaultdict to regular dict of dicts."""
    if isinstance(d, defaultdict):
        d = {k: default_to_regular(v) for k, v in d.items()}
    return d
         
# initialising_dictionary
ini_dict = {'Geeks_for_for':1,'Geeks_for_geeks':4,
            'for_geeks_Geeks':3,'geeks_Geeks_for':7}
 
# printing initial dictionary
print ("initial_dictionary", str(ini_dict))
 
 
# code to convert ini_dict to nested dictionary
# iterating_over_dict
for k, v in ini_dict.items():
     
    # splitting keys
    * keys, final_key = k.split('_')
    getFromDict(d, keys)[final_key] = v
 
# printing final dictionary
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 :

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. Create an empty dictionary d that will hold the nested dictionary.
  6. Iterate over the flattened dictionary ini_dict using a for loop.
  7. Split each key in ini_dict into a list of keys using the split method.
  8. Call the add_to_nested_dict function with the empty dictionary d, the list of keys, and the value.
  9. Print the final nested dictionary d.

Python3




def add_to_nested_dict(nested_dict, keys, value):
    """Add a value to a nested dictionary at the specified keys."""
    if len(keys) == 1:
        # base case: add value to the last key in the list
        nested_dict[keys[0]] = value
    else:
        # recursive case: create nested dictionary if it doesn't exist
        if keys[0] not in nested_dict:
            nested_dict[keys[0]] = {}
        # recursively call function with remaining keys and value
        add_to_nested_dict(nested_dict[keys[0]], keys[1:], value)
 
# initialising_dictionary
ini_dict = {'Geeks_for_for':1,'Geeks_for_geeks':4,
            'for_geeks_Geeks':3,'geeks_Geeks_for':7}
 
# printing initial dictionary
print ("initial_dictionary", str(ini_dict))
 
# create empty nested dictionary
d = {}
 
# iterating_over_dict
for k, v in ini_dict.items():
    # splitting keys
    keys = k.split('_')
    # add value to nested dictionary
    add_to_nested_dict(d, keys, v)
 
# printing final dictionary
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

  1. Create an empty dictionary.
  2. Iterate through the items in the initial dictionary using a for loop.
  3. Split the keys into a list using the split() method and store the value in a variable.
  4. Create a variable temp that references the empty dictionary.
  5. Iterate through the list of keys using a for loop.
  6. If the key exists in temp, update temp to reference the value of that key.
  7. 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.
  8. 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.
  9. Return the nested dictionary.

Python3




def add_to_nested_dict(nested_dict, keys, value):
    """Add a value to a nested dictionary at the specified keys."""
    temp = nested_dict
    for key in keys[:-1]:
        temp = temp.setdefault(key, {})
    temp[keys[-1]] = value
    return nested_dict
 
# initialising_dictionary
ini_dict = {'Geeks_for_for':1,'Geeks_for_geeks':4,
            'for_geeks_Geeks':3,'geeks_Geeks_for':7}
 
# printing initial dictionary
print ("initial_dictionary", str(ini_dict))
 
# create empty nested dictionary
d = {}
 
# iterating_over_dict
for k, v in ini_dict.items():
    # splitting keys
    keys = k.split('_')
    # add value to nested dictionary
    add_to_nested_dict(d, keys, v)
 
# printing final dictionary
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.



Last Updated : 10 Apr, 2023
Like Article
Save Article
Previous
Next
Share your thoughts in the comments
Similar Reads