I tried this for 2 days but can't figure it out in any way.
This is my JSON:
{
"items": [
{
"name":"itemA",
"bins":[
{
"bin_id":"b1",
"count":"11"
},
{
"bin_id":"b2",
"count":"22"
}
]
},
{
"name":"itemB",
"bins":[
{
"bin_id":"b5",
"count":"55"
}
]
}
]
}
Important note. Input JSON could be empty object '{}' or missing any part of the hierarchy.
I need to insert whole hierary or update final entry for "count". My input arguments are: itemName, binId and new count.
So for "itemA", "b1" and count 68. I need to set .items -> (item with name: "itemA") -> (bin with bin_id 68) -> count = 68.
I tried using INDEX to access items. And that works... but I can't seem to further access bins and update final count.
UPDATE:
I got something like this:
.items |= [INDEX(.[]?;.name) | .itemA |= .+ {name:"itemA",bins:[INDEX(.bins[]?;.bin_id) | .b1 |= .+ {bin_id:"b1",count:"188000"} | .[]]} | .[]]
But it does look ugly. Can this be done more clearly?
Given these arguments, this is the adapted, now nested approach from the previous question:
jq --arg name itemA --arg bin_id b1 --arg count 68 '
.items |= [INDEX(.[]?; .name) | .[$name] |= ({$name, bins}
| .bins |= [INDEX(.[]?; .bin_id) | .[$bin_id] |= {$bin_id, $count} | .[]]
) | .[]]
'
This can be generalized by moving the repeating code into a function, and nesting the calls:
jq --arg name itemA --arg bin_id b1 --arg count 68 '
def f(a;b;c): .[a] |= [INDEX(.[]?; .[b|keys[]]) | .[b[]] |= b+c | .[]];
f("items"; {$name}; f("bins"; {$bin_id}; {$count}))
'
Finally, making the function recursive allows for an arbitrarily deep nesting. Note the modified way providing the arguments as (container_field index_field index_value)* field value
, which now directly includes all the field names:
jq 'def f(v): .[v[0]] |= if v | length < 3 then v[1] else
[INDEX(.[]?; .[v[1]]) | .[v[2]] |= {(v[1]): v[2]} + f(v[3:]) | .[]]
end; f($ARGS.positional)' --args items name itemA bins bin_id b1 count 68
For an empty input, all of them produce:
{
"items": [
{
"name": "itemA",
"bins": [
{
"bin_id": "b1",
"count": "68"
}
]
}
]
}
And for the sample input, they output:
{
"items": [
{
"name": "itemA",
"bins": [
{
"bin_id": "b1",
"count": "68"
},
{
"bin_id": "b2",
"count": "22"
}
]
},
{
"name": "itemB",
"bins": [
{
"bin_id": "b5",
"count": "55"
}
]
}
]
}