Reshape a jq array with summarized data - json

New to jq but I've managed to group a load of data which I would now like summarized in a different format. Original data after group by and mapping:
[
{
"Agents": "a",
"Count": 1
},
{
"Agents": "b",
"Count": 50
},
{
"Agents": "c",
"Count": 25
},
{
"Agents": "d",
"Count": 1
},
{
"Agents": "e",
"Count": 4
},
{
"Agents": "f",
"Count": 4
},
{
"Agents": "g",
"Count": 4
}
]
and I would like this output:
{
"Count": 7,
"Agents": {
"a": 1,
"b": 50,
"c": 25,
"d": 1,
"e": 4,
"f": 4,
"g": 4
}
}
How exactly might I do this in jq please because it requires mapping the values as field names?

Another variant using reduce would be to do. The reduce expression takes the array as input and puts the key as the Agents and the value as its corresponding Count value.
jq '{ Count: length, Agents: (reduce .[] as $d ({}; .[$d.Agents] = $d.Count)) }'
The Object Value Iterator .[] used to construct the JSON. For a given .["a"], it returns "a" which is how the keys are constructed in the final JSON.

Use map to create an input for from_entries:
map({key: .Agents, value: .Count}) | {Count: length, Agents: from_entries}
map produces a list of objects like [{"key": "a", "value": 1}, ...]. from_entries turns that into a single object {"a": 1, ...}. In the final object, both length and from_entries get the same array as input, and their outputs are used to create the final object with Count and Agents keys.

Related

How can I get a single result object in jq?

I have a JSON file like this:
{
"cases": [
{"id": "1", "count": 2, "ignore": "f"},
{"id": "2", "count": 7, "ignore": "o"},
{"id": "3", "count": 11, "ignore": "o"}
]
}
Doing jq '.cases[] | { "\(.id)" : .count }' test.json gives
{
"1": 2
}
{
"2": 7
}
{
"3": 11
}
but I need
{
"1": 2,
"2": 7,
"3": 11
}
How can I get there?
You need to collect the results into an array and add them
.cases | map({ "\(.id)" : .count }) | add
Here's an approach using reduce which iteratively builds up the result object:
reduce .cases[] as {$id, $count} ({}; .[$id] = $count)
Demo
from_entries sounds like the obvious choice. First map your input array to an array of key-value pairs, then construct an object from this array:
.cases | map({ key: .id, value: .count }) | from_entries

How to update the value of the item in an array if it exists in json using jq?

I have a Json file which I want to update using jq. I am working on Ubuntu with jq-1.6
test_data.json
{
"A": "12",
"B": "34",
"C": [
["X", "test1"],
["Y", "test2"],
["Z", "test3"]
]
}
Now I want to update array C with new key:value pair. But, if the any of the key already exists then it's value should be updated.
update='[
["Z", "test4"],
["D", "test5"],
["E", "test6"]
]'
In this case the item Z already exists in test_data.json but update has new value for the item.
Expected output:
{
"A": "12",
"B": "34",
"C": [
["X", "test1"],
["Y", "test2"],
["Z", "test4"],
["D", "test5"],
["E", "test6"]
]
}
So far, I could do
cat test_data.json | jq --argjson val "${update}" '.C += $val')
But this is not updating value for item Z, instead adding new entry.
Can anyone please let me know how to resolve this?
Thanks in advance.
The .C array and the $update array both have arrays as items. You need to consider their first item to be a unique key, so that clashes can lead to overwrites. One way could be turning them into an INDEX object first, then add up those, and retrieve their items back into an array:
jq --argjson val "$update" '.C |= [[., $val | INDEX(.[0])] | add[]]' test_data.json
{
"A": "12",
"B": "34",
"C": [
[
"X",
"test1"
],
[
"Y",
"test2"
],
[
"Z",
"test4"
],
[
"D",
"test5"
],
[
"E",
"test6"
]
]
}

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

How to bring outer values inside an array iteration

I have a JSON in the shape
[
{
a:1,
b: [2,3]
},
{
a:4,
b: [5,6]
}
]
That I want to transform in the shape
[
[
{
a: 1,
b: 2,
},
{
a: 1,
b: 3,
},
],
[
{
a: 4,
b: 5,
},
{
a: 4,
b: 6,
},
],
]
That is I want to bring the value of the field a inside the array.
how can I do this with jq?
Try this :
jq 'map([{a,b:.b[]}])'
As #pmf pointed out, you can also update object :
jq 'map([.b=.b[]])'
You could iterate over the items using variable binding with as.
Then either update .b to have the value of its items using the update operator |=:
jq 'map([.b[] as $b | .b |= $b])'
Demo
Or create completely new objects from data collected:
jq 'map(.a as $a | [.b[] as $b | {$a,$b}])'
Demo
[
[
{
"a": 1,
"b": 2
},
{
"a": 1,
"b": 3
}
],
[
{
"a": 4,
"b": 5
},
{
"a": 4,
"b": 6
}
]
]

jq: how to change structure of JSON avoiding cartesian product?

I have thin JSON input:
[
{
"x": [
"2020-02-24T00:00:00",
"2020-02-25T00:00:00",
"2020-02-26T00:00:00"
],
"y": [
3,
2,
6
]
}
]
And I would like to obtain:
[
{
"a": "2020-02-24T00:00:00",
"b": 3
},
{
"a": "2020-02-25T00:00:00",
"b": 2
},
{
"a": "2020-02-26T00:00:00",
"b": 6
}
]
If I apply .[]|{a:.x[],b:.y[]} I obtain the cartesian product (9 items).
How to change the structure of this JSON avoiding cartesian product?
Introducing a single "$-variable" keeps things brief and straightforward:
map(range(0; .x|length) as $i
| {a: .x[$i], b: .y[$i]})
but a $-variable free solution only requires one more line:
map([.x, .y]
| transpose[]
| {a: .[0], b: .[1]})