Afzain123
Afzain123

Reputation: 34

How to convert flat list to nested dictionary

I want to know how to convert a flat list to a nested dictionary.

For example,

[1, 2, 3, 4] to {1: {2: {3: 4}}}

I tried:

nested_list = {i[1]:{{i[x]:i[x+1]} for x in range(2,len(i - 1))}}

in which i is the list.

Any help would be appreciated! Thanks!

Upvotes: -1

Views: 132

Answers (3)

Mechanic Pig
Mechanic Pig

Reputation: 7736

This process is actually a right fold (I realized this when learning Haskell). If we have a right folding function foldr that does not require an initial value, we can implement it according to the following code:

def make_nested_dict(iterable):
    return foldr(lambda x, y: {x: y}, iterable)

Unfortunately, there is no such thing in the stdlib. A simple implementation is to use functools.reduce, which is essentially a left fold. The third duality theorem (you can refer to here) states:

# There is no need to consider the initial value here
def foldr(func, lst):
    return foldl(lambda x, y: func(y, x), reversed(lst))

Therefore, we can obtain a concise implementation as follows:

from functools import reduce


def make_nested_dict(iterable):
    try:
        it = reversed(iterable)
    except TypeError:
        it = reversed(tuple(iterable))

    return reduce(lambda y, x: {x: y}, it)

Test:

>>> make_nested_dict(range(10))
{0: {1: {2: {3: {4: {5: {6: {7: {8: 9}}}}}}}}}
>>> make_nested_dict(iter(range(10)))
{0: {1: {2: {3: {4: {5: {6: {7: {8: 9}}}}}}}}}

One limitation is that for an iterative object with a length less than 2, the results it gives may not be satisfactory:

>>> make_nested_dict(range(0))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 6, in make_nested_dict
TypeError: reduce() of empty iterable with no initial value
>>> make_nested_dict(range(1))
0

Upvotes: 5

Алексей Р
Алексей Р

Reputation: 7627

Recursive option

def make_dict(x):
    def rmd(a):
        if len(a) == 2:
            return {a[0]: a[1]}
        return {a[0]: rmd(a[1:])}

    if len(x) < 2:
        return None
    if len(x) == 2:
        return {x[0]: x[1]}
    return {x[0]: rmd(x[1:])}

for i in range(5):
    lst = list(range(i))
    print(f'{lst} >> {make_dict(lst)}')

Output:

[] >> None
[0] >> None
[0, 1] >> {0: 1}
[0, 1, 2] >> {0: {1: 2}}
[0, 1, 2, 3] >> {0: {1: {2: 3}}}

Upvotes: 1

rdas
rdas

Reputation: 21275

How about a generic solution:

def make_dict(ll):
    if len(ll) < 2:
        return {}
    dd = {ll[-2]: ll[-1]}
    for i in range(3, len(ll) + 1):
        dd = {ll[-i]: dd}
    return dd


print(make_dict([1, 2, 3, 4]))

print(make_dict([1, 2, 3, 4, 5]))

print(make_dict([1, 2, 3, 4, 5, 6, 7, 8, 9]))

Result:

{1: {2: {3: 4}}}
{1: {2: {3: {4: 5}}}}
{1: {2: {3: {4: {5: {6: {7: {8: 9}}}}}}}}

Upvotes: 1

Related Questions