az5112
az5112

Reputation: 642

How to use .fieldname to jq's startswith

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_names. Is there a way I could plug in .first_name to startswith?

Upvotes: 0

Views: 53

Answers (1)

pmf
pmf

Reputation: 36391

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
  )
}

Upvotes: 2

Related Questions