【问题标题】:Remove subdict based on condition from nested dictionary根据嵌套字典中的条件删除 subdict
【发布时间】:2021-07-15 14:36:40
【问题描述】:

我有一个嵌套字典,其中包含“密度”和“温度”的(子)字典。这是结构:

profiles_dict =

{
    "density": {
        "elements": [
            {
                "layers": [
                    {
                        "top": 54.0,
                        "bottom": 3.75,
                        "value": 218.9
                    }
                ]
            }
        ],
        "type": "density"
    },
    "temperature": {
        "elements": [
            {
                "layers": []
            }
        ],
        "type": "temperature"
    }
}

正如在温度细分中看​​到的那样,“层”键包含一个空列表作为值。我的目标是创建一个循环/函数来删除字典{"layers": []}。

结果应该是这样的:

profiles_dict_new =

{
    "density": {
        "elements": [
            {
                "layers": [
                    {
                        "top": 54.0,
                        "bottom": 3.75,
                        "value": 218.9
                    }
                ]
            }
        ],
        "type": "density"
    },
    "temperature": {
        "elements": [
            ],
        "type": "temperature"
    }
}

我尝试了以下方法:

for k1, v1 in profiles_dict.items():
        for k2, v2 in v1.items():
            if type(v2) != list:
                continue
            for i in v2:
                for k3, v3 in i.items():
                    if v3 == []:
                        del(i)

通过这个循环,我可以访问{'layers': []},但是我不知道如何从字典中正确删除它。

第二种方式是基于Ajax1234的方案(Deleting Items based on Keys in nested Dictionaries in python)

profiles_dict_new = {d:{e:[{l:v for l, v in e.items() if v != []}]} for d, e in profiles_dict.items()}
print(profiles_dict_new)

这给了我以下错误:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-11-126f19ab7afd> in <module>
----> 1 profiles_dict_new = {d:{e:[{l:v for l, v in e.items() if v != []}]} for d, e in profiles_dict.items()}
      2 print(profiles_dict_new)

<ipython-input-11-126f19ab7afd> in <dictcomp>(.0)
----> 1 profiles_dict_new = {d:{e:[{l:v for l, v in e.items() if v != []}]} for d, e in profiles_dict.items()}
      2 print(profiles_dict_new)

TypeError: unhashable type: 'dict'

感谢您对所提出的任何一种方法的帮助。

【问题讨论】:

    标签: python loops dictionary nested


    【解决方案1】:

    而不是删除分配空列表

    你可以试试下面的代码:

    for k1, v1 in profiles_dict.items():
            for k2, v2 in v1.items():
                if type(v2) != list:
                    continue
                for i in v2:
                    for k3, v3 in i.items():
                        if v3 == []:
                            v1[k2] = []
    

    【讨论】:

    • 如果您有其他字典,那将无法使用。如果你只有一个,我会在成功分配后明确break。
    【解决方案2】:

    del 不会删除对象:它会删除名称。如果多个名称引用同一个对象,则其他名称保持不变。

    取消绑定i,循环变量,对底层列表没有任何作用,i 只是在下一次迭代中重新分配。您必须删除实际的列表元素 v2[index_of_i] 才能将其从列表中删除。

    为了能够安全地执行此操作,您需要替换原始列表或向后迭代列表的副本。您也可以向前迭代,但是索引需要格外小心,以确保它考虑到前面已删除的元素。

    其次,i.items() 上的循环会适得其反。您不想找到一个字典,其中包含一些带有空列表的键。你想要一本只有一个键的字典,所以不需要循环。

    for k1, v1 in profiles_dict.items():
        for k2, v2 in v1.items():
            if type(v2) != list:
                continue
            v1[k2] = [i for i in v2 if len(i) > 1 or next(i.values())]
    

    向后迭代将允许您保留原始列表对象:

    for k1, v1 in profiles_dict.items():
        for k2, v2 in v1.items():
            if type(v2) != list:
                continue
            n = len(v2) - 1  # capture before modifying
            for i, e in enumerate(reversed(v2)):
                if len(e) == 1 and not next(iter(e.values()))
                    del v2[n - i]
    

    如果您向前迭代,则必须复制列表才能正确执行此操作,并为索引做额外的簿记:

    for k1, v1 in profiles_dict.items():
        for k2, v2 in v1.items():
            if type(v2) != list:
                continue
            count = 0
            for i, e in enumerate(v2[:]):
                if len(e) == 1 and not next(iter(e.values()))
                    del v2[i - count]
                    count += 1
    

    表达式next(iter(...)) 是一种常见的习惯用法,用于从具有一个元素但不容易索引的事物中获取单个元素,例如dict 或set。 pop 之类的方法具有破坏性:如果您只想检查元素,则必须将其放回原处。 next(iter(...)) 可让您窥视收藏。

    【讨论】:

    • 感谢您提供详尽而富有教育意义的回答。
    • 但是对于你最后两个建议的解决方案,我再次收到错误:TypeError: 'dict_values' object is not an iterator。我认为这可能与您将 e.values() 作为 next() 的迭代器这一事实有关?
    • @linus 已修复。一般是next(iter(...)),而不仅仅是next(...)。这就是我在移动设备上发帖但无法测试时发生的情况。
    • 这是一种常见的习惯用法,用于非破坏性地从您不一定知道如何索引的可迭代对象中获取一个元素。您也可以通过set 进行操作。
    • 很高兴它有帮助。
    【解决方案3】:

    许多文本,但您需要从list 中删除dict?! 您的列表不是 i(列表的元素),而是 v2,因此请确保为您的 var 提供好名称并将 for 更改为 while,因为在迭代期间删除可能会导致问题。

    for k1, v1 in profiles_dict.items():
        for k2, v2 in v1.items():
            if type(v2) != list:
                continue
            i = 0
            while i < len(v2):
                for k3, v3 in v2[i].items():
                    if v3 == []:
                        v2.pop(i) #remove k3 if v3 empty from list v2 at index i
                        i -= 1 #decrease index i
                        break #cause k3, v3 in v2[i].items() does not exist anymore
                i += 1
    

    如果list 中的dict 项目v2 包含多个k3,它们不能为空并且您也不想删除它,您可以将空的存储在新的@987654332 中以备后用@remove_keys。 如果remove_keys 包含与整个dict 相同的数量,则您可以从v2 中.pop(index) 它,否则.pop(key) 所有存储的密钥仅来自当前dictv2[i]。

    for k1, v1 in profiles_dict.items():
        for k2, v2 in v1.items():
            if type(v2) != list:
                continue
            i = 0
            while i < len(v2):
                remove_keys = [] #store k3 when v3 == []
                for k3, v3 in v2[i].items():
                    if v3 == []:
                        remove_keys.append(k3)
                if len(remove_keys) == len(v2[i]): #if this would remove all keys
                    v2.pop(i) #delete whole "empty" dict again...
                    i -= 1
                else:
                    for k3 in remove_keys: #remove stored keys from dict in v2 at i
                        v2[i].pop(k3)
                i += 1
    

    【讨论】:

    • 这将删除其中有一个空列表的非空字典
    • @Mad Physicist 这是真的,但在这里并不重要,因为我需要检查列表中究竟有哪些项目,它们都是 dictst ......所以我忽略了它。让我添加一些细节来访问此行为。
    • 谢谢。直言不讳总是好的。请不要大喊大叫。
    猜你喜欢
    • 1970-01-01
    • 2020-10-20
    • 1970-01-01
    • 1970-01-01
    • 2019-06-04
    • 2018-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多