Home > Enterprise >  Why variable is needed sometimes to define a function in jq
Why variable is needed sometimes to define a function in jq

Time:02-23

The following (https://jqplay.org/s/9r9oTh1VFq)

filter:
def addvalue(f): map(.   f); addvalue(.[0])

input:
[[1,2],[10,20]]

gives error:

jq: error (at <stdin>:0): array ([1,2]) and number (1) cannot be added
exit status 5

But the this (https://jqplay.org/s/X7FDfQWubV)

filter:
def addvalue(f): map(.   f); addvalue([1,2])

input:
[[1,2],[10,20]]

output:
[
  [
    1,
    2,
    1,
    2
  ],
  [
    10,
    20,
    1,
    2
  ]
]

works as expected.

From the jq manual, the following (https://jqplay.org/s/npl2nalQyq) works too:

filter:
def addvalue($f): map(.   $f); addvalue(.[0])

input:
[[1,2],[10,20]]

output:
[
  [
    1,
    2,
    1,
    2
  ],
  [
    10,
    20,
    1,
    2
  ]
]

In the jq manual it says:

If you want the value-argument behaviour for defining simple functions, you can just use a variable...

Why do I need to use the variable $f in the function definition for addvalue(.[0]) to work?

Thanks!

CodePudding user response:

Function arguments are filters, which will take input and produce output when used in the body of the function.

To "pass" a value, you "consume" the filter immediately and assign it to a variable

def addvalue(f): f as $f | map(.   $f)

which you can write using shorthand

def addvalue($f): map(.   $f)

Without the variable, the filter .[0] is used in the call to map

map(.   .[0])

where you will now try, for example, to add [1,2] and 1 rather than the intended [1,2] [1,2]. That is, the .[0] argument is not "evaluated" to produce a value to pass to the function; the filter itself is passed as the argument and not actually used until map is called.

CodePudding user response:

The results are different because .[0] has different values depending on the context it is being evaluated in.


In your first example, the function's parameter is a filter, and it is called with an argument which also is filter. Therefore, the argument filter is only evaluated when the function's parameter is. This "late" context is the primary input's array elements. The overall evaluation is as follows:

[[1,2], [10,20]] | def addvalue(f): map(.   f); addvalue(.[0])
[[1,2], [10,20]] | map(.   .[0])
[[1,2]   .[0], [10,20]   .[0]]
[[1,2]   1, [10,20]   10]
jq: error (at <stdin>:0): array ([1,2]) and number (1) cannot be added
exit status 5

In your second example, the function's parameter is still a filter, but it is called with an argument which now is a value. Therefore, even if the function's parameter is evaluated late, it is fed with the unchanged constant value defined on top level. The overall evaluation is as follows:

[[1,2], [10,20]] | def addvalue(f): map(.   f); addvalue([1,2])
[[1,2], [10,20]] | map(.   [1,2])
[[1,2]   [1,2], [10,20]   [1,2]]
[[1,2,1,2], [10,20,1,2]]

In your third example, the function's parameter is a variable, and it is called with an argument which is a filter. Therefore, to set the function's parameter variable, the argument filter has to be evaluated immediately. This "early" context is the primary input. The overall evaluation is as follows:

[[1,2], [10,20]] | def addvalue($f): map(.   $f); addvalue(.[0])
[[1,2], [10,20]] | def addvalue($f): map(.   $f); addvalue([1,2])
[[1,2], [10,20]] | map(.   [1,2])
[[1,2]   [1,2], [10,20]   [1,2]]
[[1,2,1,2], [10,20,1,2]]
  • Related