【问题标题】:Return a list of all variable names in a python nested dict/json document in dot notation以点表示法返回 python 嵌套 dict/json 文档中所有变量名的列表
【发布时间】:2013-07-30 16:55:34
【问题描述】:

我正在寻找一个函数,它可以在 JSON 格式的 python 任意嵌套的 dict/array 上运行,并返回一个字符串列表,该列表键入它包含的所有变量名,深度无限。所以,如果对象是......

x = {
    'a': 'meow',
    'b': {
        'c': 'asd'
    },
    'd': [
        {
            "e": "stuff",
            "f": 1
        },
        {
            "e": "more stuff",
            "f": 2
        }
    ]
}

mylist = f(x) 会返回...

>>> mylist
['a', 'b', 'b.c', 'd[0].e', 'd[0].f', 'd[1].e', 'd[1].f']

【问题讨论】:

  • d 应该在列表中吗?如果没有,b 真的应该在那里吗?
  • 你的顶级对象总是一个字典吗?如果没有,你会返回什么,例如["foo", "bar"]? (['[0]', '[1]'] 大概不会是对的)。
  • 好点,@Zero。是的,我总是可以假设顶层是一个字典。

标签: python json dictionary nested-lists


【解决方案1】:
def dot_notation(obj, prefix=''):
     if isinstance(obj, dict):
         if prefix: prefix += '.'
         for k, v in obj.items():
             for res in dot_notation(v, prefix+str(k)):
                 yield res
     elif isinstance(obj, list):
         for i, v in enumerate(obj):
             for res in dot_notation(v, prefix+'['+str(i)+']'):
                 yield res
     else:
         yield prefix

例子:

>>> list(dot_notation(x))
['a', 'b.c', 'd[0].e', 'd[0].f', 'd[1].e', 'd[1].f']

【讨论】:

    【解决方案2】:

    这是一个有趣的。我使用递归解决了它。

    def parse(d):
        return parse_dict(d)
    
    def parse_dict(d):
        items = []
        for key, val in d.iteritems():
            if isinstance(val, dict):
                # use dot notation for dicts
                items += ['{}.{}'.format(key, vals) for vals in parse_dict(val)]
            elif isinstance(val, list):
                # use bracket notation for lists
                items += ['{}{}'.format(key, vals) for vals in parse_list(val)]
            else:
                # just use the key for everything else
                items.append(key)
        return items
    
    def parse_list(l):
        items = []
        for idx, val in enumerate(l):
            if isinstance(val, dict):
                items += ['[{}].{}'.format(idx, vals) for vals in parse_dict(val)]
            elif isinstance(val, list):
                items += ['[{}]{}'.format(idx, vals) for vals in parse_list(val)]
            else:
                items.append('[{}]'.format(val))
        return items
    

    这是我的结果:

    >>> parse(x)
    ['a', 'b.c', 'd[0].e', 'd[0].f', 'd[1].e', 'd[1].f']
    

    编辑

    这里再次使用生成器,因为我喜欢 F.j 的回答

    def parse(d):
        return list(parse_dict(d))
    
    def parse_dict(d):
        for key, val in d.iteritems():
            if isinstance(val, dict):
                # use dot notation for dicts
                for item in parse_dict(val):
                    yield '{}.{}'.format(key, item)
            elif isinstance(val, list):
                # use bracket notation
                for item in parse_list(val):
                    yield '{}{}'.format(key, item)
            else:
                # lowest level - just use the key
                yield key
    
    def parse_list(l):
        for idx, val in enumerate(l):
            if isinstance(val, dict):
                for item in parse_dict(val):
                    yield '[{}].{}'.format(idx, item)
            elif isinstance(val, list):
                for item in parse_list(val):
                    yield '[{}]{}'.format(idx, item)
            else:
                yield '[{}]'.format(val)
    

    同样的结果:

    >>> parse(x)
    ['a', 'b.c', 'd[0].e', 'd[0].f', 'd[1].e', 'd[1].f']
    

    【讨论】:

      【解决方案3】:

      如果你的对象的顶层可以是一个列表(数组,在 JSON 术语中),你的输出格式不起作用:例如 ["foo", "bar"] 你会在逻辑上返回 ['[0]', '[1]'],这可能不是什么你要。您也可以通过将对象名称传递给 F.J 的答案稍作修改来解决此问题:

      def paths(container, name):
          if isinstance(container, list):
              for i, element in enumerate(container):
                  for path in paths(element, "%s[%d]" % (name, i)):
                      yield path
          elif isinstance(container, dict):
              for k, element in container.items():
                  for path in paths(element, "%s.%s" % (name, k)):
                      yield path
          else:
              yield name
      

      用法:

      >>> list(paths(x, "x"))
      ['x.a', 'x.b.c', 'x.d[0].e', 'x.d[0].f', 'x.d[1].e', 'x.d[1].f']
      >>> list(paths(["foo", "bar"], "array"))
      ['array[0]', 'array[1]']
      

      Python 3.3 引入了 yield from 语法,这使得它更加简洁:

      def paths(container, name):
          if isinstance(container, list):
              for i, element in enumerate(container):
                  yield from paths(element, "%s[%d]" % (name, i))
          elif isinstance(container, dict):
              for k, element in container.items():
                  yield from paths(element, "%s.%s" % (name, k))
          else:
              yield name
      

      【讨论】:

        【解决方案4】:

        仅限 Python 3.3+:

        def f(x, parent=''):
            if isinstance(x, dict):
                if parent:
                    parent += '.'
                for key in x:
                    yield parent + key
                    yield from f(x[key], parent + key)
            elif isinstance(x, list):
                for idx, subx in enumerate(x):
                    yield from f(subx, '{}[{}]'.format(parent, idx))
        

        Python 2.x ~ Python 3.2

        def f(x, parent=''):
            if isinstance(x, dict):
                if parent:
                    parent += '.'
                for key in x:
                    yield parent + key
                    for y in f(x[key], parent + key): yield y
            elif isinstance(x, list):
                for idx, subx in enumerate(x):
                    for y in f(subx, '{}[{}]'.format(parent, idx)): yield y
        

        >>> x = {'a': 'meow', 'b': {'c': 'asd'}, 'd':[{"e":"stuff","f":1}, {"e":"more stuff","f":2}]}
        >>> list(f(x))
        ['d', 'd[0].e', 'd[0].f', 'd[1].e', 'd[1].f', 'a', 'b', 'b.c']
        

        【讨论】:

          猜你喜欢
          • 2020-09-12
          • 1970-01-01
          • 2015-03-16
          • 1970-01-01
          • 2021-11-18
          • 1970-01-01
          • 2020-10-12
          • 2017-03-20
          • 2016-04-26
          相关资源
          最近更新 更多