Saltar al contenido principal

Clase 06 — jq Avanzado

map — Transformar cada elemento

# Multiplicar cada número por 2
echo '[1,2,3,4,5]' | jq 'map(. * 2)'
# [2, 4, 6, 8, 10]

# Extraer un campo de cada objeto
echo '[{"n":"Ana","e":28},{"n":"Carlos","e":35}]' | jq 'map(.n)'
# ["Ana", "Carlos"]

# Transformar objetos
echo '[{"name":"Ana","age":28},{"name":"Carlos","age":35}]' | \
jq 'map({nombre: .name, mayor_de_edad: (.age >= 18)})'

select — Filtrar elementos

# Números mayores a 3
echo '[1,2,3,4,5]' | jq 'map(select(. > 3))'
# [4, 5]

# Filtrar por campo
echo '[{"nombre":"Ana","rol":"dev"},{"nombre":"Carlos","rol":"devops"},{"nombre":"María","rol":"dev"}]' | \
jq 'map(select(.rol == "dev"))'

# Combinar con contains
echo '[{"nombre":"Docker Compose"},{"nombre":"Docker Swarm"},{"nombre":"Kubernetes"}]' | \
jq 'map(select(.nombre | contains("Docker")))'

sort_by / group_by / unique_by

DATOS='[
{"nombre":"Carlos","edad":35,"dept":"backend"},
{"nombre":"Ana","edad":28,"dept":"backend"},
{"nombre":"María","edad":22,"dept":"frontend"},
{"nombre":"Pedro","edad":30,"dept":"devops"}
]'

# Ordenar por edad
echo "$DATOS" | jq 'sort_by(.edad)'

# Ordenar descendente
echo "$DATOS" | jq 'sort_by(.edad) | reverse'

# Agrupar por departamento
echo "$DATOS" | jq 'group_by(.dept) | map({dept: .[0].dept, miembros: map(.nombre), total: length})'

# Departamentos únicos
echo "$DATOS" | jq '[.[].dept] | unique'

reduce — Acumular valores

# Sumar todos los números
echo '[1,2,3,4,5]' | jq 'reduce .[] as $n (0; . + $n)'
# 15

# Construir un objeto a partir de un array
echo '[{"k":"a","v":1},{"k":"b","v":2}]' | \
jq 'reduce .[] as $item ({}; . + {($item.k): $item.v})'
# {"a": 1, "b": 2}

# Concatenar strings
echo '["Hola","Mundo","DevOps"]' | jq 'reduce .[] as $s (""; . + $s + " ") | rtrimstr(" ")'

String interpolation y formato

# Interpolación de strings
echo '{"nombre":"Ana","edad":28}' | jq '"Me llamo \(.nombre) y tengo \(.edad) años"'

# Formato TSV
echo '[{"a":"Ana","b":28},{"a":"Carlos","b":35}]' | jq -r '.[] | [.a, .b] | @tsv'

# Formato CSV
echo '[{"a":"Ana","b":28},{"a":"Carlos","b":35}]' | jq -r '.[] | [.a, .b] | @csv'

# Base64
echo '"hola mundo"' | jq '@base64'
echo '"aG9sYSBtdW5kbw=="' | jq '@base64d'

Condicionales avanzados

# if-then-else
echo '{"status":200}' | jq 'if .status == 200 then "OK" elif .status == 404 then "Not Found" else "Error" end'

# Operador alternativo (//) — valor por defecto
echo '{"nombre":"Ana"}' | jq '.email // "no-email@default.com"'

# try-catch
echo '{"a":"no es número"}' | jq 'try (.a | tonumber) catch "no es un número"'

Funciones de string

# Split y Join
echo '"a,b,c,d"' | jq 'split(",")' # ["a", "b", "c", "d"]
echo '["a","b","c"]' | jq 'join("-")' # "a-b-c"

# Test (regex)
echo '"user@email.com"' | jq 'test("@.*\\.com$")' # true

# Match (regex con captura)
echo '"v2.1.0"' | jq 'match("v([0-9]+)\\.([0-9]+)\\.([0-9]+)") | .captures | map(.string)'

# gsub (reemplazar con regex)
echo '"Hello World"' | jq 'gsub("o"; "0")'

# ascii_downcase / ascii_upcase
echo '"Hola Mundo"' | jq 'ascii_downcase'

# startswith / endswith
echo '"archivo.json"' | jq 'endswith(".json")' # true

Funciones matemáticas

# min / max
echo '[5,2,8,1,9]' | jq 'min' # 1
echo '[5,2,8,1,9]' | jq 'max' # 9

# min_by / max_by
echo '[{"n":"Ana","e":28},{"n":"Carlos","e":35}]' | jq 'max_by(.e) | .n'

# add (suma de array)
echo '[10,20,30]' | jq 'add' # 60

# Promedio
echo '[10,20,30]' | jq 'add / length' # 20

# floor / ceil / round
echo '3.7' | jq 'floor, ceil, round' # 3, 4, 4

Combinar archivos JSON

# Archivo 1: usuarios
echo '[{"id":1,"nombre":"Ana"},{"id":2,"nombre":"Carlos"}]' > /tmp/usuarios.json

# Archivo 2: roles
echo '[{"user_id":1,"rol":"admin"},{"user_id":2,"rol":"dev"}]' > /tmp/roles.json

# Combinar (join manual)
jq -s '
.[0] as $usuarios | .[1] as $roles |
[$usuarios[] | . as $u |
($roles[] | select(.user_id == $u.id)) as $r |
$u + {rol: $r.rol}
]
' /tmp/usuarios.json /tmp/roles.json

# Merge profundo de dos objetos
echo '{"a":1,"b":{"x":1}}' | jq '. * {"b":{"y":2},"c":3}'
# {"a":1,"b":{"x":1,"y":2},"c":3}

Ejemplo: Dashboard de Kubernetes con jq

# Listar pods con formato tabla
kubectl get pods -o json | jq -r '
.items[] |
[.metadata.name, .metadata.namespace, .status.phase,
(.status.containerStatuses[0].restartCount // 0),
.metadata.creationTimestamp] | @tsv' | column -t

# Pods con más de 5 reinicios
kubectl get pods -A -o json | jq '
[.items[] |
select(.status.containerStatuses != null) |
select([.status.containerStatuses[].restartCount] | add > 5) |
{nombre: .metadata.name, namespace: .metadata.namespace,
reinicios: [.status.containerStatuses[].restartCount] | add}]'

Ejemplo: Generar JSON desde cero

# Construir un deployment de K8s con jq
jq -n \
--arg name "mi-api" \
--arg image "mi-api:2.0" \
--argjson replicas 3 \
--argjson port 8080 \
'{
apiVersion: "apps/v1",
kind: "Deployment",
metadata: {name: $name},
spec: {
replicas: $replicas,
selector: {matchLabels: {app: $name}},
template: {
metadata: {labels: {app: $name}},
spec: {containers: [{name: $name, image: $image, ports: [{containerPort: $port}]}]}
}
}
}'