简体   繁体   English

将列表转换为字典,其中每个键都嵌套在下一个键下

[英]Convert a list into a dict where each key is nested under the next one

I want to convert this list:我想转换这个列表:

[1,2,3,4,5]

Into this dict:进入这个字典:

{ 1 :
  { 2 :
    { 3 :
      { 4 : 5 }}}}

This doesn't sound too complicated but I'm stumped when it's time to assign a value to a key deeper than the surface.这听起来并不太复杂,但是当需要为比表面更深的键分配值时,我感到很困惑。 I have a recursive function for finding how deep my dictionary goes but I don't know how to tell my algorithm to "add the new key here".我有一个递归函数来查找我的字典有多深,但我不知道如何告诉我的算法“在此处添加新键”。

You are looking for a recursive function that builds a dictionary with the first list element as a key and the transformed rest of the list as the value:您正在寻找一个递归函数,该函数构建一个字典,其中第一个列表元素作为键,列表的其余部分作为值进行转换:

l = [1, 2, 3, 4, 5]

def l2d(l):  
    if len(l) < 2: # Not good
        raise Exception("The list is too short")
    if len(l) == 2: # Base case
        return {l[0]: l[1]}
    # Recursive case
    return {l[0]: l2d(l[1:])}

l2d(l)
# {1: {2: {3: {4: 5}}}}

Another interesting approach is to use functools.reduce :另一个有趣的方法是使用functools.reduce

from functools import reduce
reduce(lambda tail,head: {head: tail}, reversed(l))
# {1: {2: {3: {4: 5}}}}

It progressively applies a dictionary construction function to the first element of the list and the rest of it.它逐步将字典构造函数应用于列表的第一个元素和其余元素。 The list is reversed first, so the construction naturally starts at the end.列表先倒序,所以构造自然从最后开始。 If the list is too short, the function returns its first element, which may or may not be desirable.如果列表太短,函数会返回它的第一个元素,这可能是也可能不是。

The "reduce" solution is MUCH FASTER , by about two orders of magnitude. “减少”解决方案要快得多,大约两个数量级。 The bottom line: avoid recursion .底线:避免递归

There is no need for recursion to solve this.不需要递归来解决这个问题。 A simple loop is sufficient.一个简单的循环就足够了。

def nest(keys):
    if not keys:
        return {}
    elif len(keys) == 1:
        return key[0]
    root = level = {}
    for key in keys[:-1]:
        level[key] = {}
        level = level[key]
    level[key] = keys[-1]
    return root

Alternatively, you can build the dict inside-out:或者,您可以从内到外构建 dict:

def nest(keys):
    if not keys:
        return {}
    current = keys[-1]
    for key in keys[-2::-1]:  # iterate keys in reverse
        current = {key: current}
    return current

That's a nice case to use the recursive logic until the length of your list is 1. For example:这是使用递归逻辑直到列表长度为 1 的好方法。例如:

Update: Fix empty list error.更新:修复空列表错误。

A = [1, 2, 3, 4, 5]

def rec_dict(listA):
    try:
        if len(listA) <= 1:
            listA = listA[0]
        else:
            listA = {listA.pop(0): rec_dict(listA)}
    except IndexError:
        pass
    return listA


print(rec_dict(A))

Which returns:返回:

{1: {2: {3: {4: 5}}}}

I want to propose another option using a different approach:我想使用不同的方法提出另一种选择:

Although it's not recommend to use ast.literal_eval , I think in this case can be useful.虽然不推荐使用ast.literal_eval ,但我认为在这种情况下可能会有用。 Additionally, You can help yourself using re.sub() + re.findall() in order to find the matches and to do the replacements correctly.此外,您可以使用re.sub() + re.findall()帮助自己找到匹配项并正确进行替换。 The idea here is using the string representation of the list.这里的想法是使用列表的字符串表示。

import ast
import re

lst = [1, 2, 3, 4, 5]
lst_str = str(lst).replace("[","{").replace("]","}")

pattern_commas = re.compile(", ")
matches = re.findall(pattern_commas, lst_str)
n = len(matches) - 1
output = re.sub(pattern_commas, ':{', lst_str, n).replace(",",":") + '}' * n
dictionary = ast.literal_eval(output)
print(dictionary)

Output:输出:

{1: {2: {3: {4: 5}}}}

EDIT : Before using this answer, please read @wjandrea's comment.编辑:在使用此答案之前,请阅读@wjandrea 的评论。 This doesn't work for all inputs.这不适用于所有输入。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM