jhnc
jhnc

Reputation: 16702

yq syntax to append a modified array element to a YAML file

Given a YAML file like:

set:
  - x: a
    y: aaa
  - x: b
    y: bbb
  - x: a
    y: ccc

I want to select elements of set where x==a, manipulate them, and append the result to the original array. For example:

set:
  - x: a
    y: aaa
  - x: b
    y: bbb
  - x: a
    y: ccc
  - x: A
    y: aaa
  - x: A
    y: ccc

I found Mike Farah's version of yq and tried:

yq '{"set":[.set[], (.set[]|select(.x=="a")|.x="A")]}'

but this changes the original, resulting in:

set:
  - x: A
    y: aaa
  - x: b
    y: bbb
  - x: A
    y: ccc
  - x: A
    y: aaa
  - x: A
    y: ccc

Upvotes: 0

Views: 1309

Answers (2)

jhnc
jhnc

Reputation: 16702

I opened an issue on github. Mike identified the cause as:

[yq] does not create a copy of the data structure when you call .set[]. In the expression where you use it twice, the elements of the splat refer to the same location in memory, so updating one updates both.

To get what you need in yq, you need to explicitly make a copy, you can do that by merging it in to an empty node

His suggestion changes my code:

old: yq '{"set":[.set[], (    .set[] |select(.x=="a")|.x="A")]}'

new: yq '{"set":[.set[], (({}*.set[])|select(.x=="a")|.x="A")]}'
                          ^^^^^^^^^^^

Given that running kislyuk/yq on my original code produces the result I expected, I think the root cause of my problem is not having appropriate sequence points for use of / assignment to .set[].

Upvotes: 0

pmf
pmf

Reputation: 36088

I just realized that my solution only worked for kislyuk/yq.

yq -y '.set += (.set | map(select(.x == "a") | .x = "A"))'

For mikefarah/yq go with @Fravadona's solution

yq '.set as $set | .set += ($set | map(select(.x=="a") | .x = "A"))'

Both output:

set:
  - x: a
    y: aaa
  - x: b
    y: bbb
  - x: a
    y: ccc
  - x: A
    y: aaa
  - x: A
    y: ccc

Upvotes: 2

Related Questions