30

I have some json data that looks like:

{
  "p": {
    "d": {
      "a" : {
        "r": "foo",
        "g": 1
      },
      "b": {
        "r": "bar",
        "g": 2
      }
    },
    "c": {
      "e": {
        "r": "baz",
        "g": 1
      }
    },
    ...
  }
}

I want something like:

{
  "d": [ 
    "a",
    "b"
  ],
  "c": [
    "e"
  ]
}

I can get the list of keys on the first level under "p" with jq '.p|keys', and the structure and keys on the second level with jq '.p|map(.|keys)', but I can't figure out how to combine it.

3 Answers3

26

Use map_values instead of map to map the values of a JSON object while preserving the keys:

jq '.p | map_values(keys)'

On jq versions lower than 1.5, map_values is not defined: instead, you can use []|=:

jq '.p | . []|= keys'
  • 1
    Apparently `map_values` doesn't exist in the version (1.4) I have here. – Henrik supports the community Nov 10 '15 at 14:57
  • 2
    Try `jq '.p | . []|= keys'` –  Nov 10 '15 at 15:21
  • Well, if I recall correctly, `map_values` is defined as `def map_values(f): . []|= f;` in jq's standard library, so both answers are equivalent; the reason the first one didn't work for you is that you're using an old version of jq. That said, I'll append this to the existing answer. –  Nov 11 '15 at 12:21
14

In general

Top level keys:

curl -s https://crates.io/api/v1/crates/atty | jq '. |= keys'

[
  "categories",
  "crate",
  "keywords",
  "versions"
]

Two levels of keys:

curl -s https://crates.io/api/v1/crates/atty | jq '.| map_values(keys)'

{
  "crate": [
    "badges",
    "categories",
    "created_at",
    "description",
    "documentation",
    "downloads",
    "exact_match",
    "homepage",
    "id",
    "keywords",
    "links",
    "max_version",
    "name",
    "newest_version",
    "recent_downloads",
    "repository",
    "updated_at",
    "versions"
  ],
  "versions": [
    0,
    1,
    2,
    3,
    4,
    5,
    6,
    7,
    8,
    9,
    10,
    11,
    12,
    13,
    14,
    15,
    16
  ],
  "keywords": [
    0,
    1,
    2
  ],
  "categories": []
}

Method versions

topLevelJsonKeys() {
 curl -s $1 | jq '. |= keys'
  # EXAMPLE: 
  # topLevelJsonKeys https://crates.io/api/v1/crates/atty
}

topLevelJsonKeys2() {
  curl -s $1 | jq '.| map_values(keys)'
  # EXAMPLE: 
  # topLevelJsonKeys2 https://crates.io/api/v1/crates/atty
}
jasonleonhard
  • 12,047
  • 89
  • 66
2

Here is a solution which uses reduce and setpath

  .p
| reduce keys[] as $k (
     .
   ; setpath([$k]; .[$k] | keys)
)
jq170727
  • 13,159
  • 3
  • 46
  • 56