计算JSON叶节点

时间:2016-06-08 15:46:13

标签: python json recursion count yield

我想计算JSON结构中的叶子数(即只有那些没有其他子元素的键)。

我找不到任何显而易见的事情,所以一直在尝试编写一个函数,但我很难找到一个有效的函数,而不使用全局变量。

这是我到目前为止所做的:

def count_leafs(nested):
  is isinstance(nested, Mapping):
    for k, v in nested.items():
      if isinstance(v, Mapping):
        for i_k, i_v in count_leafs(v):
          yield i_k, i_v
      elif isinstance(v, MutableSequence):
        for i_k in v:
          for i_i_k, i_i_v in i_k.items():
            count_leafs(i_i_v)
      else:
        yield k, v
  elif isinstance(nested, MutableSequence):
    for k in nested:
      count_leafs(k)


for k,v in count_leafs(json):
 leaf_count += 1

由于计算了一些非叶子节点,因此它并没有真正起作用,并且它不会一直递归到某些结构中。

2 个答案:

答案 0 :(得分:2)

您的伪代码过于复杂且错误。我还建议您编写紧跟PEP 8 - Style Guide for Python Code之后的代码,以供您自己和其他人阅读您编写的代码。

无论如何,作为测试用例,假设你有一些像这样的JSON数据:

json_data = {
    "glossary": {
        "title": "example glossary",
        "answer": 42,
        "boolean": True,
        "nada": None,
        "GlossDiv": {
            "GlossList": {
                "GlossEntry": {
                    "GlossDef": {
                        "GlossSeeAlso": [
                            "GML",
                            "XML"
                        ],
                        "para": "A meta-markup language, used to create markup "
                                "languages such as DocBook."
                    },
                    "GlossSee": "markup",
                    "Acronym": "SGML",
                    "GlossTerm": "Standard Generalized Markup Language",
                    "SortAs": "SGML",
                    "Abbrev": "ISO 8879:1986",
                    "ID": "SGML"
                }
            },
            "title": "S"
        }
    }
}

您可以递归计算叶子,如下所示:

from collections import Mapping, MutableSequence

def count_leaves(json_obj):

    def leaf_iterator(json_obj):
        if isinstance(json_obj, Mapping):
            for v in json_obj.values():
                for obj in leaf_iterator(v):
                    yield obj
        elif isinstance(json_obj, MutableSequence):
            for v in json_obj:
                for obj in leaf_iterator(v):
                    yield obj
        else:
            yield json_obj

    return sum(1 for leaf in leaf_iterator(json_obj))

leaf_count = count_leaves(json_data)
print('leaf count: {}'.format(leaf_count))  # -> leaf_count: 14

我在叶子计数函数中嵌套了leaf_iterator()生成器,但如果它在更大的上下文中证明是有用的,它也可以在外面定义。通过使用Python版本3.3中引入的yield from<expression>,可以在Python 3中进一步简化其中的代码。

答案 1 :(得分:2)

一般来说,我更喜欢非递归解决方案而不是递归解决方案。我的算法是这样的:

  1. 初始化队列并将json对象放入其中
  2. 队列不为空时循环
  3. 从队列中获取一个节点
    • 如果是映射,请将所有值添加到队列中以供以后处理
    • 如果它是序列或集合(注意:字符串也是序列 - 我们需要对它进行测试),我们将所有元素添加到队列中以供以后处理
    • 如果不属于上述情况,请将其计算在内
  4. 以下是代码:

    from collections import Mapping, Sequence, Set, deque
    
    def count_leaves(nested):
        queue = deque([nested])
        count = 0
        while queue:
            node = queue.popleft()
            if isinstance(node, Mapping):
                queue.extend(node.values())
            elif isinstance(node, (Sequence, Set)) and not isinstance(node, basestring):
                queue.extend(node)
            else:
                count += 1
    
        return count