crayboy
crayboy

Reputation: 13

reformat json with jq - collect results in array

I have a query that returns json like this:

{
  "hostname": "seapr1pgdb032",
  "ip": "10.215.0.6",
  "dataCenter": "seapr1"
}
{
  "hostname": "seapr1cpndb001",
  "ip": "10.203.0.41",
  "dataCenter": "seapr1"
}
{
  "hostname": "seapr1dhcp01",
  "ip": "10.205.3.212",
  "dataCenter": "seapr1"
}

The dataCenter can vary, I would like to collect all the hosts for each dataCenter into a single object like this:

{
 "dataCenter": "seapr1",
 "hosts": [
    "seapr1pgdb032",
    "seapr1cpndb001",
    "seapr1dhcp01"
  ] 
}

Working from Reshaping JSON with jq I thought this would do it:

{dataCenter: .dataCenter, hosts: [.hostname] } 

but I get three dataCenter objects, not the single consolidated one I expected:

{
  "dataCenter": "seapr1",
  "hosts": [
    "seapr1pgdb032"
  ]
}
{
  "dataCenter": "seapr1",
  "hosts": [
    "seapr1cpndb001"
  ]
}
{
  "dataCenter": "seapr1",
  "hosts": [
    "seapr1dhcp01"
  ]
}

Upvotes: 0

Views: 74

Answers (1)

peak
peak

Reputation: 116670

Here is a jq solution that avoids the built-in group_by filter for the reasons explained in the comments below.

# This stream-oriented variant of the built-in `group_by` emits a stream of arrays,
# each array representing a group. The main advantages over `group_by` are
# that no sorting is required, and the ordering of items within a group is preserved.
# There are no restrictions on f or the items in the stream;
# in particular, f may evaluate to any number of values,
# but if f evaluates to empty at any item in the stream, then that item will in effect be discarded.
# Example:
#  GROUPS_BY( 4,3,2,1; . % 2 ) => [4,2] [3,1]

def GROUPS_BY(stream; f): 
  reduce stream as $x ({};
    reduce [$x|f][] as $s (.;
        ($s|type) as $t
        | (if $t == "string" then $s
           else ($s|tojson) end) as $y
        | .[$t][$y] += [$x] ) ) 
  | .[][] ;

Using the -n command-line option, we can avoid "slurping" the input by writing the solution as follows:

GROUPS_BY(inputs; .dataCenter)
| {dataCenter: .[0].dataCenter, hosts: map(.hostname) }

Upvotes: 3

Related Questions