jq Transformations
Transformations are where jq shines. Reshape JSON into exactly the structure you need - flatten, nest, merge, split, or completely reconstruct data.
Object Construction
Build New Objects
echo '{"firstName": "John", "lastName": "Doe", "age": 30, "city": "LA"}' | jq '{
name: (.firstName + " " + .lastName),
location: .city
}'
Output:
{
"name": "John Doe",
"location": "LA"
}
Shorthand Syntax
When key matches field name:
echo '{"name": "server", "ip": "10.0.0.1", "port": 22, "extra": "ignored"}' | jq '{name, ip, port}'
Output:
{
"name": "server",
"ip": "10.0.0.1",
"port": 22
}
Dynamic Keys
echo '{"key": "hostname", "value": "server-01"}' | jq '{(.key): .value}'
Output:
{
"hostname": "server-01"
}
Nested Object Construction
echo '{"src_ip": "10.0.0.1", "src_port": 443, "dst_ip": "10.0.0.2", "dst_port": 80}' | jq '{
source: {ip: .src_ip, port: .src_port},
destination: {ip: .dst_ip, port: .dst_port}
}'
Output:
{
"source": {"ip": "10.0.0.1", "port": 443},
"destination": {"ip": "10.0.0.2", "port": 80}
}
Array Construction
Collect Values
# Collect iterated values into array
echo '[{"name":"a"},{"name":"b"},{"name":"c"}]' | jq '[.[].name]'
Output: ["a","b","c"]
Array of Objects
echo '{"users": ["alice", "bob"]}' | jq '[.users[] | {username: ., role: "user"}]'
Output:
[
{"username": "alice", "role": "user"},
{"username": "bob", "role": "user"}
]
Map
Apply transformation to each array element:
# Simple map
echo '[1,2,3]' | jq 'map(. * 2)'
# [2,4,6]
# Map with object construction
echo '[{"name":"a","val":1},{"name":"b","val":2}]' | jq 'map({id: .name, value: .val})'
Output:
[
{"id": "a", "value": 1},
{"id": "b", "value": 2}
]
Map Values (Objects)
Transform object values:
echo '{"a": 1, "b": 2, "c": 3}' | jq 'map_values(. * 10)'
Output: {"a": 10, "b": 20, "c": 30}
Select / Filter
Basic Filter
echo '[1,2,3,4,5]' | jq '[.[] | select(. > 3)]'
# [4,5]
echo '[{"status":"active"},{"status":"inactive"}]' | jq '[.[] | select(.status == "active")]'
# [{"status":"active"}]
Map + Select
echo '[{"name":"a","active":true},{"name":"b","active":false}]' | jq '
map(select(.active)) | map(.name)
'
# ["a"]
Negative Filter
echo '[{"severity":"info"},{"severity":"critical"}]' | jq '[.[] | select(.severity != "info")]'
# [{"severity":"critical"}]
Flatten
Nested Arrays
echo '[[1,2],[3,[4,5]]]' | jq 'flatten'
# [1,2,3,4,5]
# Flatten one level
echo '[[1,2],[3,[4,5]]]' | jq 'flatten(1)'
# [1,2,3,[4,5]]
Objects with Arrays
echo '{"zones": [{"name": "inside", "ips": ["10.0.0.1", "10.0.0.2"]}, {"name": "dmz", "ips": ["172.16.0.1"]}]}' | jq '
[.zones[] | .ips[]]
'
# ["10.0.0.1","10.0.0.2","172.16.0.1"]
Group By
echo '[
{"type":"web","name":"web-01"},
{"type":"db","name":"db-01"},
{"type":"web","name":"web-02"}
]' | jq 'group_by(.type)'
Output:
[
[{"type":"db","name":"db-01"}],
[{"type":"web","name":"web-01"},{"type":"web","name":"web-02"}]
]
Group + Transform
echo '[
{"type":"web","name":"web-01"},
{"type":"db","name":"db-01"},
{"type":"web","name":"web-02"}
]' | jq '
group_by(.type) | map({
type: .[0].type,
servers: [.[].name],
count: length
})
'
Output:
[
{"type": "db", "servers": ["db-01"], "count": 1},
{"type": "web", "servers": ["web-01", "web-02"], "count": 2}
]
Sort
# Sort numbers
echo '[3,1,4,1,5,9]' | jq 'sort'
# [1,1,3,4,5,9]
# Sort strings
echo '["charlie","alice","bob"]' | jq 'sort'
# ["alice","bob","charlie"]
# Sort by field
echo '[{"name":"c","val":1},{"name":"a","val":3},{"name":"b","val":2}]' | jq 'sort_by(.name)'
# sorted by name alphabetically
# Reverse sort
echo '[1,2,3]' | jq 'sort | reverse'
# [3,2,1]
Unique
# Unique values
echo '[1,2,2,3,3,3]' | jq 'unique'
# [1,2,3]
# Unique by field
echo '[{"type":"a"},{"type":"b"},{"type":"a"}]' | jq 'unique_by(.type)'
# [{"type":"a"},{"type":"b"}]
Add / Reduce
Sum
echo '[1,2,3,4,5]' | jq 'add'
# 15
echo '[{"val":1},{"val":2},{"val":3}]' | jq '[.[].val] | add'
# 6
Concatenate Arrays
echo '[["a","b"],["c","d"]]' | jq 'add'
# ["a","b","c","d"]
Reduce (Advanced)
# Manual sum with reduce
echo '[1,2,3,4,5]' | jq 'reduce .[] as $x (0; . + $x)'
# 15
# Build object from array
echo '[{"key":"a","val":1},{"key":"b","val":2}]' | jq '
reduce .[] as $item ({}; . + {($item.key): $item.val})
'
# {"a":1,"b":2}
To/From Entries
to_entries
Convert object to array of key-value pairs:
echo '{"name":"server","ip":"10.0.0.1"}' | jq 'to_entries'
Output:
[
{"key": "name", "value": "server"},
{"key": "ip", "value": "10.0.0.1"}
]
from_entries
Convert array of key-value pairs to object:
echo '[{"key":"name","value":"server"},{"key":"ip","value":"10.0.0.1"}]' | jq 'from_entries'
Output: {"name":"server","ip":"10.0.0.1"}
with_entries
Transform object via entries:
# Uppercase all keys
echo '{"name":"server","ip":"10.0.0.1"}' | jq 'with_entries(.key |= ascii_upcase)'
Output: {"NAME":"server","IP":"10.0.0.1"}
# Prefix all values
echo '{"a":"1","b":"2"}' | jq 'with_entries(.value = "prefix_" + .value)'
Output: {"a":"prefix_1","b":"prefix_2"}
Delete Keys
# Delete single key
echo '{"a":1,"b":2,"c":3}' | jq 'del(.b)'
# {"a":1,"c":3}
# Delete multiple keys
echo '{"a":1,"b":2,"c":3}' | jq 'del(.a, .c)'
# {"b":2}
# Delete nested key
echo '{"user":{"name":"jdoe","password":"secret"}}' | jq 'del(.user.password)'
# {"user":{"name":"jdoe"}}
Paths
Get All Paths
echo '{"a":{"b":1},"c":[2,3]}' | jq '[paths]'
Output:
[
["a"],
["a","b"],
["c"],
["c",0],
["c",1]
]
Get/Set by Path
# Get value at path
echo '{"a":{"b":{"c":123}}}' | jq 'getpath(["a","b","c"])'
# 123
# Set value at path
echo '{"a":{"b":{"c":123}}}' | jq 'setpath(["a","b","c"]; 456)'
# {"a":{"b":{"c":456}}}
Recursive Descent ..
Find values at any depth:
echo '{"a":{"ip":"10.0.0.1"},"b":{"c":{"ip":"10.0.0.2"}}}' | jq '.. | .ip? // empty'
Output:
"10.0.0.1" "10.0.0.2"
Practice: Log Normalization
Input (ISE Log)
{
"ise_timestamp": "2026-03-15T14:30:45Z",
"user_name": "jdoe",
"user_domain": "CHLA",
"calling_station_id": "AA:BB:CC:DD:EE:FF",
"framed_ip_address": "10.50.10.100",
"passed": false,
"failure_reason": "Certificate expired"
}
Transform to OCSF
jq '{
time: .ise_timestamp,
category_uid: 3,
class_uid: 3001,
activity_id: (if .passed then 1 else 2 end),
status: (if .passed then "Success" else "Failure" end),
status_detail: .failure_reason,
actor: {
user: {
name: .user_name,
domain: .user_domain
}
},
device: {
mac: .calling_station_id,
ip: .framed_ip_address
},
metadata: {
product: {name: "Cisco ISE", vendor_name: "Cisco"},
version: "1.0.0"
}
}'
Output (OCSF Format)
{
"time": "2026-03-15T14:30:45Z",
"category_uid": 3,
"class_uid": 3001,
"activity_id": 2,
"status": "Failure",
"status_detail": "Certificate expired",
"actor": {
"user": {
"name": "jdoe",
"domain": "CHLA"
}
},
"device": {
"mac": "AA:BB:CC:DD:EE:FF",
"ip": "10.50.10.100"
},
"metadata": {
"product": {"name": "Cisco ISE", "vendor_name": "Cisco"},
"version": "1.0.0"
}
}
Key Takeaways
-
{}for object construction - Build exact structure needed -
[]to collect - Wrap iteration in[]to create array -
map()for batch transforms - Apply to every element -
select()for filtering - Keep matching elements -
group_by()for aggregation - Group by field -
to_entries/from_entries- Transform object structure -
del()to remove - Clean up unwanted fields
Next Module
Conditionals - if-then-else, select, and alternative operator.