From e440cccc0a685d7e7879108b3abcdb3ed0c85b5d Mon Sep 17 00:00:00 2001 From: qodelicious <162776175+qodelicious@users.noreply.github.com> Date: Fri, 23 Oct 2026 05:23:52 +1100 Subject: [PATCH] docs: fix arithmetic update-assignment equivalence (#3682) The manual said `a op= b` is equivalent to `a |= . op b`. It is actually equivalent to `(b as $b | a |= . op $b)`: the right-hand side gets `.` as input, not the value being updated, and each value it outputs gives a separate result. This updates the description and adds two examples, `.a += .b` and `.a += (1,2)`. `tests/man.test` and `jq.1.prebuilt` are regenerated. Fixes #2119, fixes #2407 --- docs/content/manual/dev/manual.yml | 25 +++++++++++++++++++++++-- jq.1.prebuilt | 17 ++++++++++++++++- tests/man.test | 9 +++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/docs/content/manual/dev/manual.yml b/docs/content/manual/dev/manual.yml index 966f7138..e7150c41 100644 --- a/docs/content/manual/dev/manual.yml +++ b/docs/content/manual/dev/manual.yml @@ -3561,6 +3561,13 @@ sections: The left-hand side can be any general path expression; see `path()`. + The right-hand side of `|=` is run with the value at the path + as its input, not with `.`. This is what sets it apart from + plain assignment (`=`) and the arithmetic update-assignment + operators such as `+=`, whose right-hand side is run with `.`. + For example, `.a |= .b` sets `.a` to the `b` field of `.a`, + while `.a = .b` and `.a += .b` use the `b` field of `.`. + Note that the left-hand side of `|=` refers to a value in `.`. Thus `$var.foo |= . + 1` won't work as expected (`$var.foo` is not a valid or useful path expression in `.`); use `$var | @@ -3588,13 +3595,27 @@ sections: body: | jq has a few operators of the form `a op= b`, which are all - equivalent to `a |= . op b`. So, `+= 1` can be used to - increment values, being the same as `|= . + 1`. + equivalent to `(b as $b | a |= . op $b)`. So, `+= 1` can be + used to increment values, being the same as `|= . + 1`. + + Note that the right-hand side `b` is evaluated with the same + input as the left-hand side, not with the value at the path + being updated. So `.a += .b` adds the value of `.b` to `.a`, + whereas `.a |= . + .b` would look up `.b` inside the value of + `.a`. + If `b` outputs multiple values, then `a op= b` outputs one + result for each of them. examples: - program: .foo += 1 input: '{"foo": 42}' output: ['{"foo": 43}'] + - program: '.a += .b' + input: '{"a": 1, "b": 2}' + output: ['{"a": 3, "b": 2}'] + - program: '.a += (1,2)' + input: '{"a": 0}' + output: ['{"a": 1}', '{"a": 2}'] - title: "Plain assignment: `=`" body: | diff --git a/jq.1.prebuilt b/jq.1.prebuilt index 45056308..be0b8fab 100644 --- a/jq.1.prebuilt +++ b/jq.1.prebuilt @@ -4015,6 +4015,9 @@ This is the "update" operator \fB|=\fR\. It takes a filter on the right\-hand si The left\-hand side can be any general path expression; see \fBpath()\fR\. . .P +The right\-hand side of \fB|=\fR is run with the value at the path as its input, not with \fB\.\fR\. This is what sets it apart from plain assignment (\fB=\fR) and the arithmetic update\-assignment operators such as \fB+=\fR, whose right\-hand side is run with \fB\.\fR\. For example, \fB\.a |= \.b\fR sets \fB\.a\fR to the \fBb\fR field of \fB\.a\fR, while \fB\.a = \.b\fR and \fB\.a += \.b\fR use the \fBb\fR field of \fB\.\fR\. +. +.P Note that the left\-hand side of \fB|=\fR refers to a value in \fB\.\fR\. Thus \fB$var\.foo |= \. + 1\fR won\'t work as expected (\fB$var\.foo\fR is not a valid or useful path expression in \fB\.\fR); use \fB$var | \.foo |= \. + 1\fR instead\. . .P @@ -4044,7 +4047,10 @@ jq \'(\.\.|select(type=="boolean")) |= if \. then 1 else 0 end\' .IP "" 0 . .SS "Arithmetic update\-assignment: +=, \-=, *=, /=, %=, //=" -jq has a few operators of the form \fBa op= b\fR, which are all equivalent to \fBa |= \. op b\fR\. So, \fB+= 1\fR can be used to increment values, being the same as \fB|= \. + 1\fR\. +jq has a few operators of the form \fBa op= b\fR, which are all equivalent to \fB(b as $b | a |= \. op $b)\fR\. So, \fB+= 1\fR can be used to increment values, being the same as \fB|= \. + 1\fR\. +. +.P +Note that the right\-hand side \fBb\fR is evaluated with the same input as the left\-hand side, not with the value at the path being updated\. So \fB\.a += \.b\fR adds the value of \fB\.b\fR to \fB\.a\fR, whereas \fB\.a |= \. + \.b\fR would look up \fB\.b\fR inside the value of \fB\.a\fR\. If \fBb\fR outputs multiple values, then \fBa op= b\fR outputs one result for each of them\. . .IP "" 4 . @@ -4053,6 +4059,15 @@ jq has a few operators of the form \fBa op= b\fR, which are all equivalent to \f jq \'\.foo += 1\' {"foo": 42} => {"foo": 43} + +jq \'\.a += \.b\' + {"a": 1, "b": 2} +=> {"a": 3, "b": 2} + +jq \'\.a += (1,2)\' + {"a": 0} +=> {"a": 1} + {"a": 2} . .fi . diff --git a/tests/man.test b/tests/man.test index 2818c822..86243105 100644 --- a/tests/man.test +++ b/tests/man.test @@ -994,6 +994,15 @@ true {"foo": 42} {"foo": 43} +.a += .b +{"a": 1, "b": 2} +{"a": 3, "b": 2} + +.a += (1,2) +{"a": 0} +{"a": 1} +{"a": 2} + .a = .b {"a": {"b": 10}, "b": 20} {"a":20,"b":20}