Home > Enterprise >  How do you replace a nested array with a flattened version of itself in jq?
How do you replace a nested array with a flattened version of itself in jq?

Time:11-22

Taking a json file as input such as:

{"computers":
    [{"host":"example",
    "platform":"some_platform",
    "status":
        {"working":"yes",
        "display":["no"]},
    "description":""
    }]
}

...how can this be flattened to this form:

{"computers":
    "host":"example",
    "platform":"some_platform",
    "working":"yes",
    "display":"no",
    "description":""
}

ie. the status element has been flattened, the square brackets in "display":["no"] have been removed, and the square brackets around "computers":[...] have been removed.

I have so far tried using flatten in multiple ways, eg.:

cat ./output.json | jq '.computers|.[]|.status|flatten'

but this only outputs the flattened version of the contents of the status element. I cannot work out how to replace the contents with the flattened version.

CodePudding user response:

If you're sure the computers array will just contain a single object, you could use:

.computers |= (first | .   .status | del(.status) | (.display |= join(.)))

That will alter the value of .computers by doing to following:

  • Get the first object
  • Move everything inside .status to the object itself
  • del() the .status key
  • join() on the .display to get single value instead of the array.

Output:

{
  "computers": {
    "host": "example",
    "platform": "some_platform",
    "description": "",
    "working": "yes",
    "display": "no"
  }
}

JqPlay Demo


If there could be multiple objects in the computer array, first will ensure the first object is used.

We could also use last to get the last one:

.computers |= (last | .   .status | del(.status) | (.display |= join(.)))

Or combine map() and add to loop over all the objects, and add them together, then the the keys will be overwritten so the last value will stay visible, this might be handy if not all the objects have all the keys:

.computers |= (map(.   .status | del(.status) | (.display |= join(.))) | add)

CodePudding user response:

If your JSON structure is fixed, the following could do:

.computers
| first
| { host, platform, description }
  (.status | .display |= first)
| { computers: . }

or

.computers
| first
| del(.status)   (.status | .display |= first)
| { computers: . }

or

{
    computers: (
        .computers[0] | del(.status)   (.status | .display |= first)
    )
}

Output:

{
  "computers": {
    "host": "example",
    "platform": "some_platform",
    "description": "",
    "working": "yes",
    "display": "no"
  }
}

Another alternative is reassigning the computers property, similar to 0stone0's solution, but a little bit shorter:

.computers |= (first | del(.status)   (.status | .display |= first))
  • Related