I want to merge two JSON files, where the objects in the base file (A) are augmented by matching objects in file (B), but not overwritten where A and B have different values for the same key.
For example, I have a JSON file (A) with contents like
[
{
"name": "alpha",
"value": "apple"
},
{
"name": "beta",
"value": "banana"
},
{
"name": "gamma",
"value": "guava"
}
]
and a second JSON file (B), with the same basic structure but optionally with additional keys (metadata
in this example, but it could be anything):
[
{
"name": "alpha",
"value": "apple",
"metadata": {
"types": [
"granny"
]
}
},
{
"name": "beta",
"value": "blueberry"
},
{
"name": "omega",
"value": "orange"
}
]
I want to merge the two lists together, key/values from objects in B are added into the same object (matched by name
) in A. My JQ to achieve this is like:
jq -s '[ .[0][] as $a | .[1][] as $b | select ($a.name == $b.name) | $a * $b ]' b.json a.json
This works to add the metadata
into A and retains A’s values for other keys. However, gamma
is missing from the result. From the example above, I want a result where:
- alpha should have the metadata
- beta should be banana, not blueberry.
- gamma should be in the final list
- omega should NOT be in the final list
- Order of objects in the list should be maintained.
I want this:
[
{
"name": "alpha",
"value": "apple"
"metadata": {
"types": [
"granny"
]
}
},
{
"name": "beta",
"value": "banana"
},
{
"name": "gamma",
"value": "guava"
}
]
How do I adapt this query to also include top level objects from A, but not from B?
2
Answers
If names are guaranteed to be unique within one file, you could temporarily turn file A into an
INDEX
, so checking the containedness of a.name
becomes a matter of checking keys withhas
. Then, iterate over the items of file B usingreduce
oninput[]
, and if a name is present as key, update the according field by adding its old value to the current item (order matters). Finally, revert the object structure back to an array by collecting all field values into an array, using[.[]]
.Demo
Try this: jq ‘map(. + (input | map(select(.name == .name)))[0])’ jsonA.json jsonB.json