I have the following json file which contains this array structure:
{
"outer": [
{
"inner": [
{
"value": "val1"
},
{
"value": "val3"
}
]
},
{
"inner": [
{
"value": "val2"
},
{
"value": "val1"
}
]
},
{
"inner": [
{
"value": "val2"
},
{
"value": "val1"
},
{
"value": "val3"
}
]
}
]
}
I want to delete the inner array from the outer array whose elements have specific values and and is of certain length. E.g., if I want to delete the inner array which contains values "val1" and "val2" the result should be:
{
"outer": [
{
"inner": [
{
"value": "val1"
},
{
"value": "val3"
}
]
},
{
"inner": [
{
"value": "val2"
},
{
"value": "val1"
},
{
"value": "val3"
}
]
}
]
}
I have tried
jq 'del( .outer[]|select(.inner[0].value == "val1"))'
but I do not know how to check for the second condition, the length and on top of that the values may appear in any order.
The jq filter you are looking for is:
del(.outer[] | select(.inner | map(.value) | sort == ["val1", "val2"]))
.inner | map(.value) produces an array that contains the values associated to the value key from all objects contained by .inner.
sort is needed because == does a one-to-one comparison of arrays. This way it matches the objects contained in .inner no matter their order. Of course, you have to use a sorted array on the right-hand side (i.e. ["val1", "val2"] and not ["val2", "val1"]).
See it in action.
Here is a solution which should work on all versions of jq at least from version 1.3 onwards, and which is readily adapted to take into account additional criteria, as mentioned in the Q:
# A helper function for defining the retention criteria.
# It is assumed that the input is the array to be checked and that
# `match` is already sorted.
def retain( match ): (map(.value) | sort) != match;
.outer |= map( select( .inner | retain( ["val1", "val2"] ) ))
Related
I am looping over a list of object and for each objects I would like to get the elements in the array field with key property equal to 1, if any. I have tried array | selectattr("key", "equalto", 1) | first but it fails when there is no element satisfying the condition. I have tried using an if statement with defined condition, but it fails at the first and I can't avoid it since selectattr is a generator.
There is always the possibility to create my own first filter, but I am looking for a pure jinja2 solution.
{
"objects": [
{
"array": [
{
"key": 1
"value": 234
},
{
"key": 2
"value": 235
}
]
},
{
"array": [
{
"key": 3
"value": 256
},
{
"key": 2
"value": 231
}
]
}
]
}
Let's say I have the following JSON output:
{
"Stacks": [
{
"StackName": "hello-world",
"Tags": [
{
"Key": "environment",
"Value": "sandbox"
},
{
"Key": "Joe Shmo",
"Value": "Dev"
}
]
},
{
"StackName": "hello-man",
"Tags": [
{
"Key": "environment",
"Value": "live"
},
{
"Key": "Tandy",
"Value": "Dev"
}
]
}
]
}
How would I write a jq query to grab all StackNames for stacks that do NOT have a Tags value "Key": "Joe Shmo"? So the result would return simply hello-man.
.Stacks[]
| select( any(.Tags[]; .Key == "Joe Shmo" ) | not)
| .StackName
This checks for equality efficiently (any has short-circuit semantics), whereas contains would check for containment.
Using contains, like this:
jq -r '.Stacks[]|select(.Tags|contains([{"Key": "Joe Shmo"}])|not).StackName'
Note: -r removes the quotes from output, otherwise jq would print "hello-man" (within double quotes)
I need to remove all array elements that have the name field ending with 1.
Input:
{
"foo": "bar",
"data": {
"code": "abc123",
"items": [
{
"name": "exp1"
},
{
"name": "exp2"
},
{
"name": "exp11"
}
]
}
}
Desired output:
{
"foo": "bar",
"data": {
"code": "abc123",
"items": [
{
"name": "exp2"
}
]
}
}
My attempt:
jq 'del(.data.items[] | select(.name | endswith("1")))' input
Which results in Invalid path expression.
You can use this jq filter:
jq '.data.items|=map(select(.name|endswith("1")|not))' file
This replace .data.items with the a new array having objects whose names don't end with 1.
Your attempt will work with recent versions of jq (that is, more recent than version 1.5).
Yet another variant (perhaps the most concise robust alternative):
.data.items|=map(select(.name|test("[^1]$")))
I have a json object with a collection of additional objects.
Using jq I need to be able to modify some of the objects while keeping the others unchanged.
I keep running into issues where I can get the change, but then all the other objects disappear. Or I make the change but it impacts every other object.
Here is a sample json
{
"AAAA": {
"VALUES": {
"val_1": {
"key": "A_KEY1"
},
"val_2": {
"key": "A_KEY2"
}
}
},
"BBBB": {
"VALUES": {
"val_a": {
"key": "KEY1",
"old": [ 0, 1 ]
},
"val_b": {
"key": "KEY2",
"old": [ 2, 3 ]
}
}
}
}
What I am looking to get is no change to the AAAA object, however for each value in BBBB.VALUES I want to take the second value from the old array and create a new key value say "new"
{
"AAAA": {
"VALUES": {
"val_1": {
"key": "A_KEY1"
},
"val_2": {
"key": "A_KEY2"
}
}
},
"BBBB": {
"VALUES": {
"val_a": {
"key": "KEY1",
"old": [ 0, 1 ],
"new": 1
},
"val_b": {
"key": "KEY2",
"old": [ 2, 3 ],
"new": 3
}
}
}
}
The array is guaranteed to be two elements long, the array does not need to be modified.
I've tried many incantations of jq and just can't seem to get it to work.
Thanks
You can use [ keys[] ] to iterate over the keys under BBBB.VALUES :
jq '(.BBBB.VALUES | . [ keys[] ]) |= .+ { new: .old[1] }' data.json
If one understands with_entries/1, an easy-to-read solution is possible:
.BBBB.VALUES
|= with_entries( .value.new = .value.old[1] ) )
Or more briefly:
.BBBB.VALUES
|= with_entries( .value |= (.new = .old[1] ) )
Here is a solution which uses map_values
.BBBB.VALUES |= map_values(.new = .old[1])
Using jq, how do I select a parent object if it contains a child object that meets two filter requirements?
In this example I want to select all Subnets elements that have a child tag with key "Name" and value "TheName". My example has two subnets. The first has "TheName" in the wrong key. The second subnet has the name/value pair I am looking for. i.e. "Key": "Name", "Value": "TheName"
The following selects a subnet with the specified value in one of the tags but not the pair. It returns both subnets instead of only the second subnet.
jq '.Subnets[] | select(.Tags[].Value=="TheName")' output
How do I use jq to select only the subnets that have the name/value pair I am looking for?
{
"Subnets": [
{
"VpcId": "vpc-12345678",
"SubnetId": "subnet-1234567a",
"Tags": [
{
"Key": "IgnoreThis",
"Value": "TheName"
},
{
"Key": "Name",
"Value": "NotTheName"
}
]
},
{
"VpcId": "vpc-12345678",
"SubnetId": "subnet-1234567b",
"Tags": [
{
"Key": "IgnoreThis",
"Value": "ignore"
},
{
"Key": "Name",
"Value": "TheName"
}
]
}
]
}
The desired output would be:
{
"VpcId": "vpc-12345678",
"SubnetId": "subnet-1234567b",
"Tags": [
{
"Key": "IgnoreThis",
"Value": "ignore"
},
{
"Key": "Name",
"Value": "TheName"
}
]
}
Assuming your jq has any/2, a simple and efficient solution would be:
.Subnets[]
| select( any (.Tags[]; .Key == "Name" and .Value == "TheName") )
This produces the output you want, so I won't repeat it here.
If your jq does not have any/2, I'd suggest upgrading, but if that's inconvenient or not an option, you could use this def:
def any(f;g): reduce f as $i (false; . or ($i|g));
p.s. any(str; cond) can be read as: 'Is there any element, e, in the stream, str, such that e|cond has a value other than null or false?'
Here is a solution which uses indices
.Subnets[] | select(.Tags | indices({Key:"Name", Value:"TheName"}) != [])