Is there a special value to omit fields from jq output?

Viewed 258

I can easily conditionally set a field to null:

echo '{"id": 1, "more": "fields"}' \
  | jq '{newId: (if .id == 1 then null else .id end), newMore: .more}'

yields

{
  "newId": null,
  "newMore": "fields"
}

But how can I conditionally delete it? I'm looking for something like:

echo '{"id": 1, "more": "fields"}' \
  | jq '{
          newId: (if .id == 1 then <special-value> else .id end), 
          newMore: .more
       }'

to yield

{
  "newMore": "fields"
}

Does such a <special-value> exist? If not, what are other viable solutions? They should ideally also be usable for large objects with many additional and nested fields.

5 Answers

In that case you might want to create your object inside the if statement:

echo '{"id": 1}' | jq 'if .id == 1 then {} else {newId: .id} end'

You could adopt the convention that, if a key's value is null (or nan), the key can be deleted. To delete all the null-valued keys, you can use map_values(select(. != null)) (or map_values(select(isnan|not))).

Thus, using your example, we find:

$ echo '{"id": 1}' | jq '{newId: (if .id == 1 then null else .id end)} | map_values( select(. != null))'
{}

and:

$ echo '{"id": 1}' | jq '{newId: (if .id == 1 then nan else .id end)} | map_values( select(isnan|not))'
{}

Using jq 1.5 or later, you can use empty as the "special value" as follows:

echo '{"id": 1}' | jq '. as $in 
  | reduce "newId" as $k (.; .[$k] = if .id == 1 then empty else .id end)'

The point is that you can handle indefinitely many keys this way; for example, if you have a list, $l, of key names to be added conditionally, you would write: reduce $l[] as $k ...

You can use empty as the special value along with // {}:

$ echo '{"id": 1}' | jq '{newId: (if .id == 1 then empty else .id end) } // {} '
{}

This technique can also be used add a field conditionally, e.g.

$ echo '{"id": 1}' | jq '. += ({newId: (if .id == 1 then empty else .id end) } // {})'
{
  "id": 1
}

I now went with a solution where I conditionally add the fields in the end. Looks a little overkill for such a small object, but if you're dealing with bigger objects with a lot of additional fields, it's a very clean way.

echo '{"id": 1, "more": "fields"}' \
| jq '.id as $id
       | {newMore: .more} 
       | if $id == 1 then . else . + {newId: $id} end
     '
Related