Search code examples
jsonjqtranspose

"Transpose"/"Rotate"/"Flip" JSON elements


I would like to "transpose" (not sure that's the right word) JSON elements.

For example, I have a JSON file like this:

{
  "name": {
    "0": "fred",
    "1": "barney"
  },
  "loudness": {
    "0": "extreme",
    "1": "not so loud"
  }
}

... and I would like to generate a JSON array like this:

[
  {
    "name": "fred",
    "loudness": "extreme"
  },
  {
    "name": "barney",
    "loudness": "not so loud"
  }
]

My original JSON has many more first level elements than just "name" and "loudness", and many more names, features, etc.

For this simple example I could fully specify the transformation like this:

$ echo '{"name":{"0":"fred","1":"barney"},"loudness":{"0":"extreme","1":"not so loud"}}'| \
> jq '[{"name":.name."0", "loudness":.loudness."0"},{"name":.name."1", "loudness":.loudness."1"}]'

[
  {
    "name": "fred",
    "loudness": "extreme"
  },
  {
    "name": "barney",
    "loudness": "not so loud"
  }
]

... but this isn't feasible for the original JSON.

How can jq create the desired output while being key-agnostic for my much larger JSON file?


Solution

  • Building upon Peak's solution, here is an alternative based on group_by to deal with arbitrary orders of inner keys.

    keys_unsorted as $keys
    | map(to_entries[])
    | group_by(.key)
    | map(with_entries(.key = $keys[.key] | .value |= .value))
    

    Using paths is a good idea as pointed out by Hobbs. You could also do something like this :

    [ path(.[][]) as $p | { key: $p[0], value: getpath($p), id: $p[1] } ]
    | group_by(.id)
    | map(from_entries)