Reputation: 400
Is it possible to filter an entire array of items in JQ in only one pass? Compare the following code, which runs jq
over and over:
{
"foofoo": {
"barbar": [
{
"foo": "aaaa",
"bar": 0000
},
{
"foo": "bbbb",
"bar": 1111
},
{
"foo": "cccc",
"bar": 2222
}
]
}
}
bash array:
array=("1111" "2222")
my code is working but not very efficient and uses a lot of resources considering the array size in reality:
for k in "${array[@]}"; do
jq --argjson k "$k" '.foofoo.barbar |= map(select(.bar != $k))' json.json | sponge json.json
done
It keeps looping through the array, removing the unneeded entries and storing same file again by using sponge.
any ideas how to achieve a similar behavior with a lighter code?
Desired output:
{
"foofoo": {
"barbar": [
{
"foo": "aaaa",
"bar": 0
}
]
}
}
Upvotes: 1
Views: 295
Reputation: 116690
Constructing a dictionary object opens the door to an efficient solution. If your jq has INDEX/2
, you could use the following invocation:
jq --arg p "${arr[*]}" '
INDEX($p | split(" ")[]; .) as $dict
| .foofoo.barbar
|= map(select($dict[.bar|tostring] | not))'
If your jq does not have INDEX/2
, then now would be an excellent time to upgrade; otherwise, you could snarf its def by googling:
jq "def INDEX"
Upvotes: 0
Reputation: 92854
To improve the performance significantly use the following jq
approach (without any shell loops):
arr=("1111" "2222")
jq '($p | split(" ") | map(tonumber)) as $exclude
| .foofoo.barbar
|= map(select(.bar as $b
| any($exclude[]; . == $b) | not))' \
--arg p "${arr[*]}" file.json | sponge file.json
The output:
{
"foofoo": {
"barbar": [
{
"foo": "aaaa",
"bar": 0
}
]
}
}
Upvotes: 2
Reputation: 246764
I'm positive there are better ways to do this: I really just throw stuff at jq until something sticks to the wall ...
# 1. in the shell, construct a JSON object string from the array => {"bbbb":1,"cccc":1}
printf -v jsonobj '{%s}' "$(printf '"%q":1\n' "${array[@]}" | paste -sd,)"
# 2. use that to test for non-membership in the jq select function
jq --argjson o "$jsonobj" '.foofoo.barbar |= map(select((.bar|in($o)) == false))' json.json
outputs
{
"foofoo": {
"barbar": [
{
"foo": "0000",
"bar": "aaaa"
}
]
}
}
You don't actually show your desired output, so I assume this is what you want.
Upvotes: 1