Home > front end >  Count elements in nested JSON with jq
Count elements in nested JSON with jq

Time:12-08

I am trying to count all elements in a nested JSON-document with jq?

Given the following JSON-document

{"a": true, "b": [1, 2], "c": {"a": {"aa":1, "bb": 2}, "b": "blue"}}

I want to calculate the result 6.

In order to do this, I tried the following:

echo '{"a": true, "b": [1, 2], "c": {"a": {"aa":1, "bb": 2}, "b": "blue"}}' \
| jq 'reduce (.. | if (type == "object" or type == "array") 
      then length else 0 end) as $counts 
      (1; .   $counts)'

# Actual output: 10
# Desired output: 6

However, this counts the encountered objects and arrays as well and therefore yields 10 opposing to the desired output: 6

So, how can I only count the document's elements/leaf-nodes?

Thanks already in advance for you help!

Edit: What would be an efficient approach to count empty arrays and objects as well?

CodePudding user response:

You can use the scalars filter to find leaf nodes. Scalars are all "simple" JSON values, i.e. null, true, false, numbers and strings. Alternatively you can compare the type of each item and use length to determine if an object or array has children.

I've expanded your input data a little to distinguish a few more corner cases:

Input:

  {
    "a": true,
    "b": [1, 2],
    "c": {
      "a": {
        "aa": 1,
        "bb": 2
      },
      "b": "blue"
    },
    "d": [],
    "e": [[], []],
    "f": {}
  }

This has 15 JSON entities:

  • 5 of them are arrays or objects with children.
  • 4 of them are empty arrays or objects.
  • 6 of them are scalars.

Depending on what you're trying to do, you might consider only scalars to be "leaf nodes", or you might consider both scalars and empty arrays and objects to be leaf nodes.

Here's a filter that counts scalars:

[..|scalars]|length

Output:

6

And here's a filter that counts all entities which have no children. It just checks for all the scalar types explicitly (there are only six possible types for a JSON value) and if it's not one of those it must be an array or object, where we can check how many children it has with length.

[
  ..|
  select(
    (type|IN("boolean","number","string","null")) or
    length==0
  )
]|
length

Output:

10
  • Related