Unflatten JSON with arrays

I have a flattened JSON that I need to Unflatten.

Flattened version looks like this below - note that “Flower” represents an array hence the “0”.

{
    'Trees.ID1.Bud.Growth Season': '',
    'Trees.ID1.Bud.Axil': '',
    'Trees.ID1.Bud.Flower.0.Sex': '',
    'Trees.ID1.Bud.Flower.0.Petal Count ': '', 
    'Trees.ID1.Bud.Flower.0.Petal Colour': ''
}


I’ve managed to Unflatten, but unfortunately my approach treats the array as a structure.

{
    'Trees': {
        'ID1': {
            'Bud': {
                'Growth Season': '', 
                'Axil': '', 
                'Flower': {
                    '0': {
                        'Sex': '', 
                        'Petal Count ': '', 
                        'Petal Colour': ''
}}}}}}

What I’m trying to work out now is whether I need to adjust my flattened format, or the process I use to UNflatten ?

The Unflatten process I took from this post Python best way to unflat json?

from functools import reduce

def get_nested_default(d, path):
    return reduce(lambda d, k: d.setdefault(k, {}), path, d)

def set_nested(d, path, value):
    get_nested_default(d, path[:-1])[path[-1]] = value

def unflatten(d, separator='__'):
    output = {}
    for k, v in d.items():
        path = k.split(separator)
        set_nested(output, path, v)
    return output

Would appreciate any pointers as to best flatten/UNflatten in order to preserve the arrays.

Python

from functools import reduce

def get_nested_default(d, path):
    return reduce(lambda d, k: d.setdefault(k, {}), path, d)

def set_nested(d, path, value):
    get_nested_default(d, path[:-1])[path[-1]] = value

def unflatten(d, separator='__'):
    output = {}
    for k, v in d.items():
        path = k.split(separator)   
        # Check if the last element is a digit (array index)
        if path[-1].isdigit():
            # Remove the last element if it's a digit (array index)
            path = path[:-1]
        set_nested(output, path, v)
    return output

Changes made:

  1. Check for digit: Inside the loop, we added a check to see if the last element in the path list (obtained by splitting the key) is a digit using path[-1].isdigit().
  2. Remove last element (if digit): If the last element is a digit, it means it’s an array index. So, we remove it from the path list using path = path[:-1]. This ensures that the digit becomes the index for the array instead of a separate key.

Flattening:

You don’t necessarily need to modify your flattening process as long as it follows consistent formatting. As long as your flattened JSON uses a consistent separator (like “.”) between keys and uses numeric indices for arrays (e.g., “Trees.ID1.Bud.Flower.0”), the improved unflattening function will handle it correctly.

Here’s how the unflattening process works with this example:

  1. Iterate through flattened keys:
  • 'Trees.ID1.Bud.Growth Season': ''
  • 'Trees.ID1.Bud.Axil': ''
  • 'Trees.ID1.Bud.Flower.0.Sex': ''
  • 'Trees.ID1.Bud.Flower.0.Petal Count ': ''
  • 'Trees.ID1.Bud.Flower.0.Petal Colour': ''
  1. Split key by separator:
  • For each key, it’s split using the separator (e.g., “.”).
  1. Check for digit:
  • For keys like “Trees.ID1.Bud.Flower.0”, the check identifies the last element (“0”) as a digit.
  1. Remove last element (if digit):
  • Since it’s a digit, the last element (“0”) is removed from the path, resulting in [“Trees”, “ID1”, “Bud”, “Flower”].
  1. Set nested value:
  • The value from the flattened JSON is set at the appropriate nested location using set_nested(output, path, value).
  • This creates the “Flower” array within the “Bud” structure and sets the values under the “0” index.

With these changes, your unflattening function will correctly create the desired nested structure with the “Flower” array.