JSLT tips: the for-loop and filtering arrays
JSLT is a JSON transformation language. One pattern I use a lot: build an array with let, then use a for-loop with an if to only include items that pass a condition. No separate filter step—you output and filter in one go.
Simple example
Input:
{
"items": [
{ "name": "a", "count": 2 },
{ "name": "b", "count": 0 },
{ "name": "c", "count": 1 }
]
}
JSLT: only include objects where count is truthy:
{
"active": [ for (.items) . if (.count) ]
}
Output:
{
"active": [
{ "name": "a", "count": 2 },
{ "name": "c", "count": 1 }
]
}
Pattern: [ for (<array>) . if (<condition>) ] — loop over the array, output the current element (.) only when the condition holds. Result is a new array with no nulls or extra logic.
Generalizing: build then filter in the output
When you have a list of name/value pairs (or similar) and only want to keep entries with a value, build the array in a let, then filter in the for-loop.
Input:
{
"id": "123",
"label": "Widget Co",
"code": "W-001",
"description": "",
"serviceRef": "svc-1",
"resourceKey": "res-1",
"tagValue": "100"
}
Template:
let attributes = [
{ "name": "ID", "value": .id },
{ "name": "LABEL", "value": .label },
{ "name": "CODE", "value": .code },
{ "name": "DESCRIPTION", "value": .description }
]
let tags = [
{ "name": .tagValue, "type": "TAG", "resourceType": "Tag" }
]
{
"resourceType": "Resource",
"name": .resourceKey,
"attributes": [ for ($attributes) . if (.value) ],
"tags": [ for ($tags) . if (.name) ]
}
attributesandtagsare built once withlet.[ for ($attributes) . if (.value) ]keeps only objects wherevalueis non-null/non-empty.[ for ($tags) . if (.name) ]does the same forname.
You define the shape of each entry in one place and drop empty ones in the for-loop, without a separate filter step.
More for-loop patterns
1. Filter by multiple conditions
Same [ for (...) . if (...) ] shape; the condition can combine fields:
[ for ($items) . if (.type == "foo" and .kind == "bar") ]
Use and / or as needed. Result: only elements that match all (or any) of the criteria.
2. Filter over a nested array
Run the for-loop over a path that’s already narrowed down (e.g. first match, then its children):
let matches = [ for ($topologies) . if (.type == "foo") ]
let links = $matches[0].links
[ for ($links) . if (.source == "bar") ]
First line gets the list of matching parents; then you take one (e.g. [0]) and loop over .links (or any nested array) and filter that. Handy when you “get the first X, then work with its Y.”
3. Nested for: “items in A that match something in B”
Bind a value from the outer element and use it in the inner loop (e.g. find all items in one list whose id appears in another):
flatten([
for ($listA)
let id = .id
[ for ($listB) . if ($id == .id) ]
])
Outer loop: for each element in $listA, set id = .id. Inner loop: over $listB, keep only elements where .id equals that id. You get an array of arrays (one per outer item), so flatten(...) turns it into a single list of matches. Same idea works for “shared” or “intersection” logic.
4. Dedupe by key
If the previous step (or any step) leaves duplicates by a key (e.g. tid), use an object as a key-value map and then take values:
let withDuplicates = [ ... ]
[ for ({ for ($withDuplicates) .tid : . }) .value ]
Duplicate .tid overwrites in the object, so you end up with one entry per key. See JSLT: removing duplicates by key for more.
5. Guard the result
When the result might be null or you must return an array:
if ($result) $result else []
Use this at the end of a function so callers always get an array when there are no matches.
Try it: JSLT demo playground — paste your input and template and run.