Get all values and nested values that match a key name - json

I have some data that looks like:
{
"hello": {
"foo": "x",
"y": "z"
},
"foo": "a",
"bar": {
{
"foo": "b"
}
}
}
How can I get all values with key foo, wherever they are?

You can search using recursive decent
Solution
jq '..|.foo?'
Demo
https://jqplay.org/s/Xp64LfJFBc
Documentation
https://stedolan.github.io/jq/manual/#RecursiveDescent:..

Related

How to sort a JSON object with sub-objects based on an attribute of its sub-objects?

I have a list of objects that I need to index into by one of its attributes, the name in the below example. So I'm storing the list as an object instead of an array:
{
"Foo": {
"name": "Foo",
"date": "2022-08-21"
},
"Bar": {
"name": "Bar",
"date": "2022-08-20"
}
}
How can I sort the sub-objects by a different property of theirs (i.e. date in the above example), when serializing this JSON?
By definition, the keys within a JSON object are semantically unordered, so implementations are not required to support explicit sorting of such keys in any way.
However, as of version 1.4, jq does preserve the order of keys within objects, and also respects the order in which they are added. So, for the problem at hand, it becomes a matter of converting the above representation into an array, sorting it, and converting it back to a JSON object:
jq 'to_entries | sort_by(.value.date) | from_entries'
Explanation
First, convert the object with sub-objects into an array of key/value pairs with to_entries. The input becomes:
[
{
"key": "Foo",
"value": {
"obj1.name": "Foo",
"obj1.date": "2022-08-21"
}
},
{
"key": "Bar",
"value": {
"obj2.name": "Bar",
"obj2.date": "2022-08-20"
}
}
]
Since we now have an array, we can sort it with an arbitrary sub-object selector in sort_by, in my example, by .value.date, the output becomes:
[
{
"key": "Bar",
"value": {
"obj2.name": "Bar",
"obj2.date": "2022-08-20"
}
},
{
"key": "Foo",
"value": {
"obj1.name": "Foo",
"obj1.date": "2022-08-21"
}
}
]
Now it's a matter of converting the key/value form back to the object form with from_entries. The output becomes:
{
"Bar": {
"name": "Bar",
"date": "2022-08-20"
},
"Foo": {
"name": "Foo",
"date": "2022-08-21"
}
}

jq: how to replace keys with values ​from other keys whose ​are in strings of some array

Consider following array:
{
"A": 100,
"B": 200,
"C": "ccc",
"arr": [
{
"result": ".R1.R3",
"fmt": "%s::%s::baz",
"vals": [".A", ".B"]
},
{
"result": ".R2.R4",
"fmt": "%s/%s",
"vals": [".A", ".C"]
}
]
}
I need to replace keys according some format with values ​​from other keys whose ​​are in strings of some array.
Desired output:
{
"A": 100,
"B": 200,
"C": "ccc",
"R1": {"R3": "100::200::baz"},
"R2": {"R4": "100/ccc"}
}
You didn't specify what language you use in the .vals array items to reference into the document. If you were trying to execute (arbitrary) jq code from there, know that jq cannot do that with code provided as a string value. jq also doesn't provide printf-style substitutions using %s (and others). Therefore, you either need to re-implement a whole bunch of (third-party) functionality, or revert to a simpler scheme describing your references and substitutions.
For the sake of simplicity, this solution just removes the first character (the dot) from the .vals array items and treats the result as top-level field name, and then simply replaces each occurrence of a literal %s with the next value. This should give you an overview of the general technique.
. as $top | reduce .arr[] as $a (del(.arr); .[$a.result] = (
reduce $a.vals[][1:] as $val ($a.fmt; sub("%s"; $top[$val] | #text))
))
{
"A": 100,
"B": 200,
"C": "ccc",
".R1": "100::200::baz",
".R2": "100/ccc"
}
Demo
One quite simple way of improving the reference language is to instead use path expressions jq provides functions for. They are represented as arrays with field names as strings items and array indices as number items. .A would become ["A"], .A[3].B would become ["A",3,"B"], and so on. Thus, assume your input looked like this:
{
"A": 100,
"B": 200,
"C": "ccc",
"arr": [
{
"result": ".R1",
"fmt": "%s::%s::baz",
"vals": [["A"], ["B"]]
},
{
"result": ".R2",
"fmt": "%s/%s",
"vals": [["A"], ["C"]]
}
]
}
Then you could use getpath to evaluate the given path expressions as above:
. as $top | reduce .arr[] as $a (del(.arr); .[$a.result] = (
reduce $a.vals[] as $path ($a.fmt; sub("%s"; $top | getpath($path) | #text))
))
{
"A": 100,
"B": 200,
"C": "ccc",
".R1": "100::200::baz",
".R2": "100/ccc"
}
Demo
Edit: As the question has been modified with the .result value now also being subject to reference interpretation, measures taken for .vals therefore apply to it as well. This implies changing the suggested source document format to use path expressions as in "result": ["R1", "R3"], and changing the assignment in the suggested code from .[$a.result] = ... to setpath($a.result; ...):
{
"A": 100,
"B": 200,
"C": "ccc",
"arr": [
{
"result": ["R1", "R3"],
"fmt": "%s::%s::baz",
"vals": [["A"], ["B"]]
},
{
"result": ["R2", "R4"],
"fmt": "%s/%s",
"vals": [["A"], ["C"]]
}
]
}
. as $top | reduce .arr[] as $a (del(.arr); setpath($a.result;
reduce $a.vals[] as $path ($a.fmt; sub("%s"; $top | getpath($path) | #text))
))
{
"A": 100,
"B": 200,
"C": "ccc",
"R1": {
"R3": "100::200::baz"
},
"R2": {
"R4": "100/ccc"
}
}
Demo

Get array with all values for certain key in JSON wih JQ

Say I have the following JSON:
{
"a": 0,
"b": "c",
"d": {
"e": {
"f": "g",
"comments": {
"leading": "Lorem ipsum"
},
"h": {
"i": {
"j": [
1,
2
]
},
"comments": {
"trailing": "dolor sit"
}
}
},
"comments": {
"leading": "amet."
}
}
}
I want to get an array with the values of all the fields named comments (which can be nested in any level). So, in this case I want to get:
[
{
"leading": "Lorem ipsum"
},
{
"trailing": "dolor sit"
},
{
"leading": "amet."
}
]
The order of the array doesn't matter.
How can this be achieved with jq? I have only performed basic stuff with it and haven't been able to produce anything close to what I need.
Thanks in advance ☺️
You can use the getpath function. Use paths to identify all the paths leading upto .comments and get the paths' value
jq '[ getpath ( paths | select( .[-1] == "comments" ) ) ]'
Or use a recursive descent to filter objects containing .comments and get its value
jq '[ recurse | select(has("comments")?).comments ]'

Conditional deletion from array of field A with condition on field B

Let's say I have a json with an array inside. Say that the elements of this array are objects with keys A and B. I would like to remove the B objects on the elements where A objects meet a certain condition.
For example, I would like to remove the B objects where A is greater than 5, transforming
{
"title": "myTitle",
"myArray": [
{
"A": 1,
"B": "foo"
},
{
"A": 4,
"B": "bar"
},
{
"A": 7,
"B": "barfoo"
},
{
"A": 9,
"B": "foobar"
}
]
}
into
{
"title": "myTitle",
"myArray": [
{
"A": 1,
"B": "foo"
},
{
"A": 4,
"B": "bar"
},
{
"A": 7
},
{
"A": 9
}
]
}
The task seems easy enough and if I had't have to keep the A's it would be a simple del(select..) thing. There surely must be an elegant way to do this as well?
Thank you!
You can still use a del(select..) thing.
.myArray[] |= del(select(.A > 5) .B)
demo at jqplay.org

jq - nested dictionary parser and extract key

I am trying to parse with jq the following structure:
{
"a": {
"sensitive": false,
"type": "string",
"value": "mykeypair"
},
"b": {
"sensitive": false,
"type": "string",
"value": "123"
}
}
and get this as an output:
{
"a": "mykeypair",
"b": "123"
}
I would like the key and as a value, the value of the field 'value'.
Any idea?
Cheers,
If you're merely getting the value of every value in the root object, you could use map_values/1 to get those values.
map_values(.value)
I think this is what you are looking for:
[ to_entries[] | .value = .value.value ] | from_entries
A simpler way:
with_entries(.value |= .value)
Check the result here:
https://jqplay.org/s/uHqfdPoF3e