【问题标题】:Shifting Levels in Nested Dictionary in Python在 Python 中的嵌套字典中移动级别
【发布时间】:2021-10-26 22:14:43
【问题描述】:

我在 Python 的深度嵌套字典中有一些来自远程服务器的数据。由于数据收集过程(我无法控制),这个字典的几个级别被包装在一个不必要的字典中,带有一个名为"__collections__" 的单个键。例如,字典是这样的

{"data_level_1":
    {"__collections__":
        {"data_level_2": ...}
    }
}

当我真正想要的是

{"data_level_1":
    {"data_level_2": ...}
}

我需要一种递归迭代嵌套字典的方法,以将那些包装的字典“移动”一级,同时摆脱 "__collections__" 包装字典。这是我的尝试:

import collections.abc

def remove_repeat_named_level(dictionary, key):
    q = list(dictionary.items())
    for v, d in q:
        if isinstance(d, MutableMapping):
            for nv, nd in d.items():
                if isinstance(nd, MutableMapping):
                    if v==key:
                        nd = remove_repeat_named_level(nd, key)
                        q.append((nv, nd))
                        if (v, d) in q: q.remove((v, d))
                    elif nv==key:
                        nd = remove_repeat_named_level(nd, key)
                        q.append((v, nd))
                        if (v, d) in q: q.remove((v, d))
                elif v==key:
                    q.append((nv, nd))
                    if (v, d) in q: q.remove((v, d))
    return dict(q)

其中dictionary 是嵌套字典,key 是我要删除的包装字典中单个键的名称(在本例中为collections)。

这在一些简单的测试用例上效果很好,例如:

test_key = "A"
test_dict = {"A": 
                {"B": 
                    {"A": 
                        {"i": 
                            {"A": 
                                {"One": 
                                    {"A": 
                                        {"alpha": "a", 
                                         "beta": "b"
                                        }
                                    }, 
                                 "Two": 2
                                 }
                             }, 
                         "ii": 2
                         }
                     }
                 }
             }

remove_repeat_named_level(test_dict, test_key)

返回预期结果:

{'B': {'i': {'One': {'alpha': 'a', 'beta': 'b'}, 'Two': 2}, 'ii': 2}

但是,当我通过函数传递带有数据的嵌套字典时,递归似乎在某个级别停止:

字典:

test_d2 = {"__collections__":
               {"tasks": 
                  {"task1":
                       {"__collections__":
                          {"subjects":
                              {"subject1":
                                  {"date": 1,
                                   "time": 1,
                                   "__collections__":
                                       {"surveys":
                                           {"survey1":
                                               {"survey_data":
                                                    {"Q1": {"Response": 1},
                                                     "Q2": {"Response": 2}
                                                    }
                                               },
                                                "__collections__": {}
                                           }
                                       }
                                  }
                              }
                          }
                       }
                  }
               }
         }

预期:

               {"tasks": 
                    {"task1":
                          {"subjects":
                              {"subject1":
                                  {"date": 1,
                                   "time": 1,
                                   {"surveys":
                                       {"survey1":
                                           {"survey_data":
                                                {"Q1": {"Response": 1},
                                                 "Q2": {"Response": 2}
                                                }
                                           }
                                       }
                                    }
                                  }
                              }
                          }
                    }
               }

结果:

{"tasks": 
              {"task1":
                  {"subjects":
                      {"subject1":
                          {"date": 1,
                           "time": 1,
                           "__collections__":
                               {"surveys":
                                   {"survey1":
                                       {"survey_data":
                                            {"Q1": {"Response": 1},
                                             "Q2": {"Response": 2}
                                            }
                                       },
                                        "__collections__": {}
                                   }
                               }
                          }
                      }
                  }
              }
         }

为了弄清楚这一点,我已经绞尽脑汁好几个小时了。为什么递归会在某个点停止?有没有我没有考虑到的情况?

【问题讨论】:

  • 您最后的预期结果不是有效的 python 字典。你不能在没有密钥的情况下在字典中只拥有一个赤裸裸的{"surveys":

标签: python dictionary recursion nested


【解决方案1】:

您的小示例和您的实际数据之间的区别在于,在您的小示例中,坏键始终在其字典中单独存在;而在您的实际数据中,坏键有时会与字典中的其他键混合在一起。

你可以大大简化你的递归函数:

def remove_repeat_named_level(d, bad_key):
  if not isinstance(d, dict):
    return d
  else:
    new_d = {k: remove_repeat_named_level(v, bad_key) for k,v in d.items() if k != bad_key}
    if bad_key in d:
      new_d.update(remove_repeat_named_level(d[bad_key], bad_key))
    return new_d

使用您的数据进行测试:

>>> remove_repeat_named_level(test_d2, '__collections__')
{'tasks': {'task1': {'subjects': {'subject1': {'date': 1, 'time': 1, 'surveys': {'survey1': {'survey_data': {'Q1': {'Response': 1}, 'Q2': {'Response': 2}}}}}}}}}

【讨论】:

    【解决方案2】:

    您可以使用递归生成器函数:

    def remove_keys(d):
       if not isinstance(d, dict):
          yield d
       else:
          for a, b in d.items():
             if a == "__collections__":
                 yield from remove_keys(b)
             else:
                 yield (a, k[0] if len(k:=list(remove_keys(b))) == 1 and not isinstance(k[0], tuple) else dict(k))
    

    import json
    r = {'data_level_1': {'__collections__': {'data_level_2': None}}}
    test_d2 = {'__collections__': {'tasks': {'task1': {'__collections__': {'subjects': {'subject1': {'date': 1, 'time': 1, '__collections__': {'surveys': {'survey1': {'survey_data': {'Q1': {'Response': 1}, 'Q2': {'Response': 2}}}, '__collections__': {}}}}}}}}}}
    print(json.dumps(dict(remove_keys(r)), indent=4))
    print(json.dumps(dict(remove_keys(test_d2)), indent=4))
    

    输出:

    {
        "data_level_1": {
            "data_level_2": null
        }
    }
    

    {
        "tasks": {
            "task1": {
                "subjects": {
                    "subject1": {
                        "date": 1,
                        "time": 1,
                        "surveys": {
                            "survey1": {
                                "survey_data": {
                                    "Q1": {
                                        "Response": 1
                                    },
                                    "Q2": {
                                        "Response": 2
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-09-21
      • 1970-01-01
      • 2014-05-27
      • 2021-01-28
      • 2019-10-03
      • 1970-01-01
      • 2018-05-31
      • 2022-01-15
      • 2021-09-30
      相关资源
      最近更新 更多