mmuurr
mmuurr

Reputation: 1580

jq: Lifting fields from array of objects into the parent object

I have a JSON object an array at the top-level, and each array entry is an object with a nested array of objects as one of the fields. I'd like to "lift" some fields from the sub-arrays into the objects within the first array. It's confusing to write, so, here's my input:

{ "emails": [
  { "email":"[email protected]",
    "events":[
      { "type":"open", "time":"t1", "ignore":"this" },
      { "type":"click", "time":"t2", "ignore":"this" } ] },
  { "email":"[email protected]",
    "events":[
      { "type":"open", "time":"t3", "ignore":"this" },
      { "type":"click", "time":"t4", "ignore":"this" },
      { "type":"open", "time":"t5", "ignore":"this" } ] }
] }

What I'd like to receive as output is:

[
  { "email":"[email protected]", "type":"open", "time":"t1" },
  { "email":"[email protected]", "type":"click", "time":"t2" },
  { "email":"[email protected]", "type":"open", "time":"t3" },
  { "email":"[email protected]", "type":"click", "time":"t4" },
  { "email":"[email protected]", "type":"open", "time":"t5" }
]

I can jq this with multiple pipes already, like so:

 [ .emails[] | { email:.email, event:(.events[] | { type:.type, time:.time }) } | { email:.email, type:.event.type, time:.event.time } ]

... but this seems way too verbose. Is there an easier way to 'lift' those type and time fields from the deepest objects to the object one-level up? When I try to use the .events[] iterator twice, I wind up with the Cartesian product of events, which is wrong :-/

I must be missing some simpler way (than my 'intermediate-object' approach above) to achieve this ... anyone know of a better way?

Upvotes: 4

Views: 519

Answers (1)

peak
peak

Reputation: 116740

Let's proceed in two steps: first, the (heavy) lifting, and secondly, the (light) trimming.

Lifting

.emails | map( {email}  + .events[])

or equivalently:

[.emails[] | {email}  + .events[]]

Notice that {"email": .email} has been abbreviated to {email}.

Trimming

We can delete the "ignore" key using del(.ignore). With an eye to efficiency, we arrive at the following solution:

.emails | map( {email}  + (.events[] | del(.ignore) ) )

Upvotes: 2

Related Questions