I'd like to default values in place on jq, and return root document.
What I have (note that some property is missing from the inner object) is following, but length is not fixed:
[{"foo":{}},{"foo":{"a":1}},{"foo":{"b":4,"c":23}},{"foo":{"a":15,"b":2,"c":33}}]
What I want (default missing property with 0
) for above example:
[
{
"foo": {
"a": 0,
"b": 0,
"c": 0
}
},
{
"foo": {
"a": 1,
"b": 0,
"c": 0,
}
},
{
"foo": {
"a": 0,
"b": 4,
"c": 23
}
},
{
"foo": {
"a": 15,
"b": 2,
"c": 33
}
}
]
I've tried something like jq '.[] | (.foo | getpath(["a"]) //= 0)' < in > out
, but this returns stream of the inner object instead of root document; this is not what I wanted because there are other property to grouping record, I would like to group by it later.
If fields clash on a merge, the right one overwrites the left one. You can utilize this fact by putting default values to the left, and merge with current values on the right. The kind of iteration over "current values" depends on your structure and your prerequisites.
A merge, for example, may (*
) or may not (+
) be deep. Here are examples for both: The first one updates the .foo
field by (shallow) merging it with an object just containing values for a
, b
, and c
. The second one updates the root object by (deep) merging it with an object containing values for foo.a
, foo.b
, and foo.c
. Both approaches output the original root level.
map(.foo |= {a:0, b:0, c:0} + .)
map({foo: {a:0, b:0, c:0}} * .)
Alternatively, if you want a path-based approach, you could provide a list of paths to iterate over, and set values at those paths as needed. The following examples make use of the alternative operator //
from your own approach (which does set the default value even if the original one indeed exists if it evaluates to null
or false
):
map(reduce(
["foo", "a"],
["foo", "b"],
["foo", "c"]
) as $p (.; setpath($p; getpath($p) // 0)))
You may also provide and set the default values individually:
map(reduce(
[["foo", "a"], 0],
[["foo", "b"], 0],
[["foo", "c"], 0]
) as [$p, $v] (.; setpath($p; getpath($p) // $v)))
In any case, use map(...)
(or .[] |= ...
, or [.[] | ...]
) instead of just .[] | ...
in order to process the top-level array's items and retain the array structure.
Given your sample input, all of these approaches output the same result (with variations in field order for the latter two, path-based examples, as ↑here the default value in some cases is "introduced later" than the original ones, but they are, nevertheless, logically equivalent):
[
{
"foo": {
"a": 0,
"b": 0,
"c": 0
}
},
{
"foo": {
"a": 1,
"b": 0,
"c": 0
}
},
{
"foo": {
"a": 0,
"b": 4,
"c": 23
}
},
{
"foo": {
"a": 15,
"b": 2,
"c": 33
}
}
]