Reputation: 95335
I have, for complicated reasons involving a trip from an Apple plist through xml2json
, a number of JSON files with data in this form:
{
"key": [ "key1", "key2", "key3" ],
"string": [ "value1", "value2", "value3" ]
}
And I would like to convert that to a normal JSON object:
{
"key1": "value1",
"key2": "value2",
"key3": "value3"
}
After some head-banging, I came up with this jq
program to do the trick:
jq '. as $d|[range(.key|length)|{"key":$d.key[.],"value":$d.string[.]}]|from_entries'
That works, but it seems a little convoluted. I was wondering if there were a cleaner solution?
This is indeed similar to this question, but the difference is that this is an object with named key and value elements instead just an array containing the two arrays directly.
Upvotes: 1
Views: 178
Reputation: 14715
Here is a solution which uses reduce with a state object holding an iteration index and a result object. It iterates over .key
setting corresponding values in the result from .string
.string as $v
| reduce .key[] as $k (
{idx:0, result:{}}; .result[$k] = $v[.idx] | .idx += 1
)
| .result
Upvotes: 0
Reputation:
The script you provide is already pretty good! This variation of your script saves the index into the variable instead of the input object, which feels more natural to read for me. It then creates an array of one key objects and adds them together.
jq '[range(.key | length) as $i | {(.key[$i]): .string[$i]}] | add'
When I first looked at this issue, I though that a zip
builtin would improve the situation. Then I remembered: there is already a zip
builtin! It's just called transpose
. Using it, you can create a script such as this:
jq '[.key, .string] | transpose | map({key: .[0], value: .[1]}) | from_entries'
It seems easier to follow to me as well, although it is quite long too; I assume, however, that the focus is readability and not character count. Of course, you can also mix up both solutions:
jq '[.key, .string] | transpose | map({(.[0]): .[1]}) | add'
Upvotes: 4