I have a JSON document holding configurations with macros. I need to expand macros that reference other elements. For simplicity, consider this input document.
[
{
"first": "Tim",
"Full": "{first} {last}",
"last": "Smith"
},
{
"first": "Jane",
"Full": "{first} {last}",
"last": "Doe"
}
]
Performance is not paramount here so I don't mind blindly checking for every element occurring in every other element.
I worked out the following logic as a proof of concept. But can't figure out how to add a nested loop on $lookup
to update the other with_entries
value.
jq '
.[]
| . as $lookup
| with_entries(
.value=(
.value
| sub("{first}"; $lookup.first)
)
)
'
JQ processes streams of values/documents and it feels like I now need to work with two streams. Which I hoped to accomplish by using $lookup
for the back reference. Now I am stuck.
SOLUTION
oguz ismail provided a great solution for the original question (Please give them a +1) that uses map_values(). It is very clear and I wanted to include it here for reference. You will note that it uses a named group (?<found_key>.*?)
in the regular expression (see oniguruma's named group)
map(. as $lookup | map_values(gsub("\\{(?<found_key>.*?)\\}"; $lookup[.found_key])))
I asked how to process when there are non-string elements in the structure. Here is an example that includes an array of colors:
[
{
"first": "Tim",
"Full": "{first} {last}",
"last": "Smith",
"colors": ["red", "blue"]
}
]
oguz ismail provided a solution for this structure as well that only attempts to modify elements that are strings:
map(
. as $lookup
| (.[] | strings)
|= gsub("\\{(?<found_key>.*?)\\}"; $lookup[.found_key])
)
You can use gsub
with a named capture group for expanding all the macros in a single run without hardcoding their names. And, with_entries
doesn't help at all in this case; use map_values
instead.
map(. as $lookup | map_values(gsub("\\{(?<found_key>.*?)\\}"; $lookup[.found_key])))