Reputation: 3081
Given master.yml ...
containers:
- name: project-a
items:
- CCC
- name: project-z
items:
- CCC
- DDD
... and an update.yml ...
- name: project-z
items:
- CCC
- EEE
... I want to merge it into the correct entry. This would give:
containers:
- name: project-a
items:
- CCC
- name: project-z
items:
- CCC
- DDD
- EEE
The following yq 4 works if the update was for project-a
yq ea 'select(fileIndex==0) * {"containers":select(fileIndex==1)}' master.yml update.yml
but, with the provided project-z
update, it incorrectly replaces the first array entry (you end up with two project-z
s).
Upvotes: 1
Views: 3166
Reputation: 2564
This is similar to another stackoverflow question in which kev had a really neat solution for (I think).
I've since added his solution to yq docs here: https://mikefarah.gitbook.io/yq/operators/multiply-merge#merge-arrays-of-objects-together-matching-on-a-key
In your case, this will only work if the second file matched the structure of the first, (that is, it also has 'containers' as a parent):
yq eval-all '
(
((.containers[] | {.name: .}) as $item ireduce ({}; . *+ $item )) as $uniqueMap
| ( $uniqueMap | to_entries | .[]) as $item ireduce([]; . + $item.value)
) as $mergedArray
| select(fi == 0) | .containers = $mergedArray
` file.yaml file2.yaml
containers:
- name: project-a
items:
- CCC
- name: project-z
items:
- CCC
- DDD
- CCC
- EEE
Basically it works by reducing into a merged map based on name (as you mention this would be much easier if that was already the case) and then converting that back to an array.
Disclaimer: I wrote yq
Upvotes: 3
Reputation: 3081
After a deep-dive into the manuals, I have this :
yq ea 'select(fi==1).0.name as $p | (select(fi==0).containers.[] | select(.name==$p)) = (select(fi==1) | .0 | . headComment="AUTO-UPDATED") | select(fi==0)' master.yml update.yml
which replaces rather than merges project-z
by first searching name
matching update.yml
then completely replacing the content.
I understand the root cause being data is formatted as a list where it should be a dictionary (name
is unique).
This would merge trivially and be better in future too!
containers:
project-a:
items:
- CCC
project-z:
items:
- CCC
- DDD
- EEE
Upvotes: 2