Working with objects: keys, entries, merge

jq · Practical · 10 min

Cleaning, renaming and combining JSON objects is needed for config files, API payloads and reports.

Basic object functions:

  • keys returns sorted key names (keys_unsorted keeps input order), has("k") tests for a key, length counts keys.
  • del(.password, .internal) removes keys; pick(.a, .b.c) (1.7+) keeps only the given paths.
  • to_entries converts {"a":1} into [{"key":"a","value":1}], and from_entries converts back.
  • with_entries(f) is to_entries | map(f) | from_entries: the standard tool to rename, filter or transform keys.

Examples: with_entries(select(.value != null)) drops null fields; with_entries(.key |= ascii_upcase) uppercases keys; with_entries(select(.key | startswith("_") | not)) drops private keys.

Merging: + merges two objects, with the right side winning for duplicate keys, shallowly. * merges recursively, combining nested objects: perfect for applying an override on top of default config. add on an array of objects merges them all with +.

To combine two files, read both and merge: jq -s '.[0] * .[1]' defaults.json overrides.json.

Object key order is preserved in output, but most jq functions that list keys sort them; add -S to sort all output keys for stable diffs.

Example

echo '{"name":"api","port":8080,"_debug":true,"db":{"host":"x","pool":5},"tls":null}' > c.json
jq -c 'keys' c.json                                # ["_debug","db","name","port","tls"]
jq -c 'del(._debug, .tls)' c.json                  # {"name":"api","port":8080,"db":{"host":"x","pool":5}}
jq -c 'with_entries(select(.value != null))' c.json | jq -c 'has("tls")'   # false
jq -c 'with_entries(select(.key | startswith("_") | not)) | keys' c.json
# ["db","name","port","tls"]
jq -c '{db: .db} + {db: {pool: 20}}' c.json        # {"db":{"pool":20}}
jq -c '{db: .db} * {db: {pool: 20}}' c.json        # {"db":{"host":"x","pool":20}}
jq -c 'to_entries[0]' c.json                       # {"key":"name","value":"api"}

Remember

  • keys, has, del, pick for inspecting and trimming
  • with_entries(f) renames/filters keys via {key, value}
  • + merges shallowly (right wins), * merges recursively

Watch out for

  • Using + to apply nested config overrides and losing sibling keys; use * for deep merges.

Check yourself

  1. What is `{"a":{"x":1}} * {"a":{"y":2}}`?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: jqlang.org/manual/

Working with objects: keys, entries, merge · jq · Topolearn