Flatten a JSON document using jq

Dror picture Dror · Jul 11, 2014 · Viewed 24k times · Source

I'm considering the following array of JSON objects:

[
  {
    "index": "index1",
    "type": "type1",
    "id": "id1",
    "fields": {
      "deviceOs": [
        "Android"
      ],
      "deviceID": [
        "deviceID1"
      ],
      "type": [
        "type"
      ],
      "country": [
        "DE"
      ]
    }
  },
  {
    "index": "index2",
    "type": "type2",
    "id": "id2",
    "fields": {
      "deviceOs": [
        "Android"
      ],
      "deviceID": [
        "deviceID2"
      ],
      "type": [
        "type"
      ],
      "country": [
        "US"
      ]
    }
  }
]

and I would like to flatten it to get:

[
  {
    "index": "index1",
    "type": "type",
    "id": "id1",
    "deviceOs": "Android",
    "deviceID": "deviceID1",
    "country": "DE"
  },
  {
    "index": "index2",
    "type": "type",
    "id": "id2",
    "deviceOs": "Android",
    "deviceID": "deviceID2",
    "country": "US"
  }
]

I'm trying to work with jq but I fail to flatten the "fields". How should I do it? At the moment I'm interested in command-line tools, but I'm open to other suggestions as well.

Answer

Jeff Mercado picture Jeff Mercado · Jul 12, 2014

This one was a tricky one to craft.

map
(
    with_entries(select(.key != "fields"))
    +
    (.fields | with_entries(.value = .value[0]))
)

Let's break it down and explain the bits of it

  1. For every item in the array...

    map(...)
    
  2. Create a new object containing the values for all except the fields property.

    with_entries(select(.key != "fields"))
    
  3. Combine that with...

    +
    
  4. Each of the fields projecting each of the values to the first item of each array

    (.fields | with_entries(.value = .value[0]))