Reputation: 131
I'm trying to use jq to convert some command-line flags into a JSON equivalent.
The flags look like this, where the idea is to convert the (optional) f flag into a JSON "foo" field, and the (optional) b flag into a JSON "bar" field:
{
"flags": [
"f1",
"b2",
"f3b4",
"b6f5"
]
}
Getting the foo fields is easy:
.flags[] | match("f([0-9][0-9]*)") | .captures[0].string | tonumber | { "foo": . }
Same for the bar fields (please say if there are better ways to do this with jq):
.flags[] | match("b([0-9][0-9]*)") | .captures[0].string | tonumber | { "bar": . }
How can I merge the output of these two filters together so that each input flags line gets mapped to a single JSON object with none / one / both of the optional fields?
The two relevant mechanisms are jq's comma operator (to share a single stream between multiple filters) and jq's + operator (to merge together objects into a single object). Applying the comma operator is straightforward:
.flags[] | (match("f([0-9][0-9]*)") | .captures[0].string | tonumber | { "foo": . }), (match("b([0-9][0-9]*)") | .captures[0].string | tonumber | { "bar": . })
However, this yields a separate object for each match:
{
"foo": 1
}
{
"bar": 2
}
{
"foo": 3
}
{
"bar": 4
}
{
"foo": 5
}
{
"bar": 6
}
So the specific problem here is how to join these two objects together using the + operator. The final output I'm trying to get here is where the foo and bar fields sit together in the same object:
{
"foo": 1
}
{
"bar": 2
}
{
"foo": 3,
"bar": 4
}
{
"foo": 5,
"bar": 6
}
What is the best way to achieve this with jq?
Upvotes: 2
Views: 2190
Reputation: 2045
The capture function seems suited to your task.
From the manual: capture(regex; flags)
"Collects the named captures in a JSON object, with the name of each capture as the key, and the matched string as the corresponding value."
jq '.flags[]
| capture("(?<foo>^f\\d+$)"),
capture("(?<bar>^b\\d+$)"),
capture("(?<foo>f\\d+)(?<bar>b\\d+)"),
capture("(?<bar>b\\d+)(?<foo>f\\d+)")
| .[] |= ( sub("\\D"; "") | tonumber )'
The capture lines create these objects:
{
"foo": "f1"
}
{
"bar": "b2"
}
{
"foo": "f3",
"bar": "b4"
}
{
"bar": "b6",
"foo": "f5"
}
The last line updates the values in those objects by removing non-digits and converting the result to a number.
Upvotes: 2