yq — Transforms

to_entries / from_entries

These are inverse operations that convert between maps and arrays of key-value pairs. This is the fundamental yq transform — most filtering depends on it.

to_entries — map to key-value array
cat <<'YAML' > /tmp/ports.yml
https: 443
ssh: 22
dns: 53
YAML
yq 'to_entries' /tmp/ports.yml
# Output:
# - key: https
#   value: 443
# - key: ssh
#   value: 22
# - key: dns
#   value: 53
from_entries — key-value array back to map
cat <<'YAML' > /tmp/ports.yml
https: 443
ssh: 22
dns: 53
YAML
yq 'to_entries | map(select(.value > 100)) | from_entries' /tmp/ports.yml
# Output:
# https: 443

The pattern to_entries | map(transform) | from_entries is so common it has a shorthand: with_entries.

with_entries

Filter map keys — keep only port attributes
yq '.asciidoc.attributes | with_entries(select(.key | test("^port-")))' docs/antora.yml
# Output:
# port-https: "443"
# port-ssh: "22"
# port-kerberos: "88"
# port-ldap: "389"
# ...

with_entries(f) is exactly to_entries | map(f) | from_entries. Use it when you want a filtered/transformed map, not an array.

Rename keys — strip prefix
yq '.asciidoc.attributes | with_entries(select(.key | test("^port-"))) | with_entries(.key |= sub("^port-"; ""))' docs/antora.yml
# Output:
# https: "443"
# ssh: "22"
# kerberos: "88"
# ldap: "389"
# ...

|= is the update operator — it modifies in place rather than replacing. sub("pattern"; "replacement") does regex substitution on a string.

map

Transform every element in a sequence
cat <<'YAML' > /tmp/hosts.yml
- name: ise-01
  ip: 10.50.1.20
- name: vault-01
  ip: 10.50.1.60
- name: vyos-01
  ip: 10.50.1.2
YAML
yq 'map(.name + " -> " + .ip)' /tmp/hosts.yml
# Output:
# - ise-01 -> 10.50.1.20
# - vault-01 -> 10.50.1.60
# - vyos-01 -> 10.50.1.2
Map with string interpolation
cat <<'YAML' > /tmp/hosts.yml
- name: ise-01
  ip: 10.50.1.20
- name: vault-01
  ip: 10.50.1.60
YAML
yq -r 'map("ssh evan@" + .ip + " # " + .name)[]' /tmp/hosts.yml
# Output:
# ssh evan@10.50.1.20 # ise-01
# ssh evan@10.50.1.60 # vault-01

sort_by

Sort entries by key name
yq '.asciidoc.attributes | to_entries | sort_by(.key) | .[:5]' docs/antora.yml
# Output: first 5 attributes alphabetically
# - key: ad-dc-hostname
#   value: home-dc01.inside.domusdigitalis.dev
# - key: ad-dc-ip
#   value: "10.50.1.50"
# ...
Sort by value (numeric context)
cat <<'YAML' > /tmp/ports.yml
- name: https
  port: 443
- name: ssh
  port: 22
- name: dns
  port: 53
- name: ldaps
  port: 636
YAML
yq 'sort_by(.port)' /tmp/ports.yml
# Output (ascending by port number):
# - name: ssh
#   port: 22
# - name: dns
#   port: 53
# - name: https
#   port: 443
# - name: ldaps
#   port: 636
Reverse sort
cat <<'YAML' > /tmp/ports.yml
- name: https
  port: 443
- name: ssh
  port: 22
- name: dns
  port: 53
YAML
yq 'sort_by(.port) | reverse' /tmp/ports.yml
# Output: descending by port number

group_by

Group array elements by a field
cat <<'YAML' > /tmp/vlans.yml
- host: ise-01
  vlan: 10
- host: vault-01
  vlan: 10
- host: vyos-01
  vlan: 1
- host: k3s-master
  vlan: 20
YAML
yq 'group_by(.vlan)' /tmp/vlans.yml
# Output:
# - - host: vyos-01
#     vlan: 1
# - - host: ise-01
#     vlan: 10
#   - host: vault-01
#     vlan: 10
# - - host: k3s-master
#     vlan: 20

group_by returns an array of arrays, grouped by the field value.

Count per group
cat <<'YAML' > /tmp/vlans.yml
- host: ise-01
  vlan: 10
- host: vault-01
  vlan: 10
- host: vyos-01
  vlan: 1
- host: k3s-master
  vlan: 20
YAML
yq '[group_by(.vlan)[] | {"vlan": .[0].vlan, "count": length}]' /tmp/vlans.yml
# Output:
# - vlan: 1
#   count: 1
# - vlan: 10
#   count: 2
# - vlan: 20
#   count: 1

unique_by

Deduplicate by a field
cat <<'YAML' > /tmp/entries.yml
- host: ise-01
  role: pan
- host: ise-01
  role: mnt
- host: vault-01
  role: secrets
YAML
yq 'unique_by(.host)' /tmp/entries.yml
# Output:
# - host: ise-01
#   role: pan
# - host: vault-01
#   role: secrets

unique_by keeps the first occurrence of each unique value.

flatten

Flatten nested arrays
cat <<'YAML' > /tmp/nested.yml
- - a
  - b
- - c
  - d
- - e
YAML
yq 'flatten' /tmp/nested.yml
# Output:
# - a
# - b
# - c
# - d
# - e
Flatten one level only
cat <<'YAML' > /tmp/deep.yml
- - - deeply
    - nested
YAML
yq 'flatten(1)' /tmp/deep.yml
# Output:
# - - deeply
#   - nested