In the jq script below I want to print either the concatenation of the first_name
and the last_name
or just the last_name
.
$ echo '{"first_name": "John", "last_name": "Johnson"}' | jq -c '{
"name": (if (.last_name | startswith(.first_name))
then .last_name
else .first_name + " " + .last_name
end)
}'
The error is
jq: error (at <stdin>:1): Cannot index string with string "first_name"
The jq command fails in the call to startswith
- as if startswith
only accepted string literals. If I change startswith(.first_name)
to startswith("John")
then the expression compiles and works as expected.
In the real-world example, there is many input records and many different first_name
s. Is there a way I could plug in .first_name
to startswith
?
You're using startswith
right, but with the preceding |
you're starting a new context (holding the value of .last_name
) in which .first_name
isn't valid anymore.
Generally speaking, to reference a value at a later time (when it has gone out of context), bind a variable to it when it's still in context, and use that instead later:
if .first_name as $fn | .last_name | startswith($fn) then …
You may also bind the whole object at an earlier stage (e.g. if you happen to need multiple references to it or its parts):
. as $obj | {name: (if $obj.last_name | startswith($obj.first_name) then …)}
Or define your own function that takes two parameters (e.g. if you need this construct more often):
# taking two values as parameters which retain their binding
def startswith($whole; $part): $whole | startswith($part);
# or carrying over the context to allow for functional parameters
def startswith(whole; part): . as $ctx | whole | startswith($ctx | part);
# then
{
name: (
if startswith(.last_name; .first_name)
then .last_name else "\(.first_name) \(.last_name)"
end
)
}