How to insert a field at the top of an object? - json

This is my code
element='{"x":"zero"}'
example='[
{
"a":"one",
"b":"two",
"c":"three"
},
{
"d":"four",
"e":"five",
"f":"six"
}]'
jq --argjson el "$element" '.[] += $el' <<< $example
It currently outputs
[
{
"a":"one",
"b":"two",
"c":"three",
"x":"zero"
},
{
"d":"four",
"e":"five",
"f":"six",
"x":"zero"
}
]
But i would like to have the "x":"zero" be the first element of all array elements, not the last.
Inverting the variable with the .[] iterator would make no sense. Any idea how this can be done?

In order for the new field to appear at the top, the object that holds it must appear first in the addition statement. For example:
$ jq --argjson el "$element" 'map($el + .)' <<< $example
[
{
"x": "zero",
"a": "one",
"b": "two",
"c": "three"
},
{
"x": "zero",
"d": "four",
"e": "five",
"f": "six"
}
]

Related

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

use jq to extract ranking from a list

I want to extract a ranking from a list of elements inside a json object whose order shows the position of the item in the respective category. That should be done by using only command line tools.
E.g.
I have a file with these 2 samples (each json object should be one-line but parsed with indentation here for readability ) :
{
"category":"triathlon",
"athletes_list":[
{
"athlete_name": "Ubain Solt"
}
,
{
"athlete_name": "Jon Snow"
}
,
{
"athlete_name": "Mickey Mouse"
}
]
}
{
"category":"swimming",
"athletes_list":[
{
"athlete_name": "Picheal Phelms"
},
{
"athlete_name":"Lacky Kedetie"
}
]
}
and want as output:
{"a":"Ubain Solt", "r":0, "c":"triathlon"}
{"a":"Jon Snow", "r":1, "c":"triathlon"}
{"a": "Mickey Mouse", "r":2, "c":"triathlon"}
{"a": "Picheal Phelms", "r":0, "c":"swimming"}
{"a": "Lacky Kedetie", "r":1, "c":"swimming"}
Requirements are to use jq and solution must be 1-liner.
jq -s '.[] | .category as $c | .athletes_list | to_entries[] | ({ a: .value.athlete_name, r: .key, c: $c })' triathlon swimming
Will produce:
{
"a": "Ubain Solt",
"r": 0,
"c": "triathlon"
}
{
"a": "Jon Snow",
"r": 1,
"c": "triathlon"
}
{
"a": "Mickey Mouse",
"r": 2,
"c": "triathlon"
}
{
"a": "Picheal Phelms",
"r": 0,
"c": "swimming"
}
{
"a": "Lacky Kedetie",
"r": 1,
"c": "swimming"
}
The 'trick' is to use to_entries so we can use .key as the 'index'.

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 ]'

jq: sort object values

I want to sort this data structure by the object keys (easy with -S and sort the object values (the arrays) by the 'foo' property.
I can sort them with
jq -S '
. as $in
| keys[]
| . as $k
| $in[$k] | sort_by(.foo)
' < test.json
... but that loses the keys.
I've tried variations of adding | { "\($k)": . }, but then I end up with a list of objects instead of one object. I also tried variations of adding to $in (same problem) or using $in = $in * { ... }, but that gives me syntax errors.
The one solution I did find was to just have the separate objects and then pipe it into jq -s add, but ... I really wanted it to work the other way. :-)
Test data below:
{
"": [
{ "foo": "d" },
{ "foo": "g" },
{ "foo": "f" }
],
"c": [
{ "foo": "abc" },
{ "foo": "def" }
],
"e": [
{ "foo": "xyz" },
{ "foo": "def" }
],
"ab": [
{ "foo": "def" },
{ "foo": "abc" }
]
}
Maybe this?
jq -S '.[] |= sort_by(.foo)'
Output
{
"": [
{
"foo": "d"
},
{
"foo": "f"
},
{
"foo": "g"
}
],
"ab": [
{
"foo": "abc"
},
{
"foo": "def"
}
],
"c": [
{
"foo": "abc"
},
{
"foo": "def"
}
],
"e": [
{
"foo": "def"
},
{
"foo": "xyz"
}
]
}
#user197693 had a great answer. A suggestion I got in a private message elsewhere was to use
jq -S 'with_entries(.value |= sort_by(.foo))'
If for some reason using the -S command-line option is not a satisfactory option, you can also perform the by-key sort using the to_entries | sort_by(.key) | from_entries idiom. So a complete solution to the problem would be:
.[] |= sort_by(.foo)
| to_entries | sort_by(.key) | from_entries

jq update list elements based on values

Is it possible to use jq to turn the following JSON data
[
{
"a": null,
"b": [
{
"c": "cc",
"d": "dd1"
},
{
"c": "cc",
"d": "dd1",
"e": "ee",
"f": "ff"
}
]
},
{
"b": [
{
"c": "cc",
"d": "dd2",
"e": "ee",
"f": "ff"
}
]
}
]
into
[
{
"a": null,
"b": [
[
"cc", "d1"
],
[
"cc", "d1", "ff"
]
]
},
{
"b": [
[
"cc", "d2", "ff"
]
]
}
]
?
Note that the purpose is to reduce the b list with certain elements of its items based on a condition. The condition assigns the string d1 if the value of d is dd1, otherwise d2 is assigned if dd2 is present.
The following unsuccessful attempt demonstrates the idea:
$ jq -r '.[].b[] = [.[].b[].c, ?, .[].b[].f?]'
This is probably not the best way to do what you want, but it does get the desired output and it has the conditional you asked about.
jq '.[].b |= map(
[ .c,
(if .d == "dd1" then "d1" elif .d == "dd2" then "d2" else . end),
.f // empty
] )'
Without changing the value of the d key, you would use this jq filter:
jq '.[].b[]|=(to_entries|map(.value))' file
That updates the b array into all values of the inner objects.
If you want to update the d, you could use this filter:
jq '.[].b[] |= (to_entries|
map(
if(.key=="d") then
.value|=sub("d+";"d")
else .
end
|.value)
)' file
that adds a check if the key is d the associated value is updated to remove the duplicated d character from the string. This requires jq to support for regex (to be able to use the sub function).