Combine the value from a key with all array entries - jq

I have json input as follows:
[{
"a": "123",
"b": [
"xyz",
"uvw"
]
}, {
"a": "456",
"b": [
"ghi"
]
}]
and I'd like to produce a list where each object's "a" is combined with each element of "b" using a delimiter. Is this possible to do using jq?
123|xyz
123|uvw
456|ghi

You can change the delimiter on the fly if you parameterize it.
$ jq -r --arg delim '|' '.[] | "\(.a)\($delim)\(.b[])"' input.json

Related

Matching When Value Is List

I have an API dump file that is similar to this.
{
"abc": {
"Code": "ABC",
"Type": [] },
"def": {
"Code": "DEF",
"Type": [
"A"
]
},
"ghi": {
"Code": "GHI",
"Type": [
"B"
]
},
"jkl": {
"Code": "JKL",
"Type": [
"A",
"B"
]
},
"mno": {
"Code": "MNO",
"Type": [ "Universal" ]
}
}
I am trying to extract objects, and get Code keys based on certain Type matches.
For example.
Trying to where Type matches "A", "B", "A, B", or Universal, I am getting objects that contain both, or nothing in certain cases.
Here is what I tried.
jq -r '.[] | select(.Type[] == "A") | .Code' /tmp/test.json
I get
DEF JKL
Which is unexpectedly matching Type ["A"} and Type ["A", "B"]
jq -r '.[] | select(.Type[] == "B") | .Code' /tmp/test.json
I get.
GHI JKL
Which is unexpectedly matching Type ["B"} and Type ["A", "B"]
jq -r '.[] | select(.Type[] == "A, B") | .Code' /tmp/test.json
Matches nothing.
This works as expected.
jq -r '.[] | select(.Type[] == "Universal") | .Code' /tmp/test.json
MNO
If you wanted exact match, save following into script.jq :
.[] | select(.Type == $match) | .Code
Then test with
$ jq -r --argjson match '["A"]' -f script.jq test.json
DEF
$ jq -r --argjson match '["B"]' -f script.jq test.json
GHI
$ jq -r --argjson match '["A", "B"]' -f script.jq test.json
JKL
The jq program
.[] | select(any(.Type[]; IN("A","B","A, B","Universal"))) | .Code
produces:
"DEF"
"GHI"
"JKL"
"MNO"

jq: list users belonging to a specific group in array

input json:
[
{
"user": "u1"
},
{
"user": "u2",
"groups": [
{
"id": "100001",
"name": "G1"
},
{
"id": "100002",
"name": "G2"
}
]
},
{
"user": "u3",
"groups": [
{
"id": "100001",
"name": "G1"
}
]
}
]
I want to find all users belonging to specific group (searching by group name or group id in the groups array)
$ jq -r '.[]|select(.groups[].name=="G1" | .user)' json
jq: error (at json:27): Cannot iterate over null (null)
Desired output format when searching of example group G1 would be:
u2
u3
Additional question:
Is it possible to produce comma-separated output u2,u3 without using external utilities like tr?
Better enter your serach data from parameters using --arg and use any to avoid duplicate outputs if both inputs match:
jq -r --arg id "" --arg name "G1" '
.[] | select(.groups | map(.id == $id or .name == $name) | any)? | .user
'
u2
u3
Demo
Using ? as the Optional Object Identifier-Index operator, you could do a select as below
map(select(.groups[].name == "G1")? | .user)
and un-wrap the results from the array by using [] at the end of the filter. To combine multiple selection conditions use the boolean operators with and/or inside the select statement
See demo on jqplay

JQ how to combine heterogeneous objects into one array?

How could I use JQ to parse the following JSON object and produce the output below?
JSON Input:
{
"key1": {
"a": "A"
},
"key2": {
"b": "123"
},
"key3": {
"c": ["C1", "C2"]
}
}
Desired Output:
[
"a": "A",
"b": 123,
"c": "C1",
"c": "C2"
]
The following program produces the output shown below it:
def q: "\"\(.)\"";
.[]
| to_entries[]
| (.key|q) as $k
| if .value|type == "array"
then .value[] | "\($k): \(q)"
else "\($k): \(.value|q)"
end
Output:
"a": "A"
"b": "123"
"c": "C1"
"c": "C2"
This, or something very much like it, should be sufficient for using in a bash script, but if you really want the format shown in the Q, feel free to fiddle around. A more useful way to spend your time would probably be to read up on jq-bash interoperability, e.g. here on SO:
Is there a way to output jq into multiple variables for bash script?
get field from json and assign to variable in bash script?
... and many others.

jq how to merge array objects into single object

in jq its possible to add objects using the + operator
if you have an array
[
{
"a": "value"
},
{
"b": "value"
},
{
"c": "value"
}
]
I want to convert it into a single object { a:"value", b:"value", c:"value" }
I can use the following filter .[0] + .[1] + .[2], but i want to do it for the whole array without specifying all the indexes.
You can use reduce:
reduce .[] as $o ({}; . + $o)
returns:
{
"a": "value",
"b": "value",
"c": "value"
}
The simplest way is just to call add filter.
"The filter add takes as input an array, and produces as output the elements of the array added together. This might mean summed, concatenated or merged depending on the types of the elements of the input array - the rules are the same as those for the + operator (described above)."
Source: https://stedolan.github.io/jq/manual/#add
$ cat test.json
[
{
"a": "value"
},
{
"b": "value"
},
{
"c": "value"
}
]
$ jq 'add' test.json
{
"a": "value",
"b": "value",
"c": "value"
}
As mentioned by peak in the comment, you can even skip wrapping add filter with the quotes:
$ jq add test.json
{
"a": "value",
"b": "value",
"c": "value"
}
In case you want to merge all objects in array recursively (didn't find any similar answer here):
jq 'reduce .[] as $x ({}; . * $x)'

Use jq to combine two arrays of objects on a certain key

I am trying to use jq to solve this problem.
Suppose I have the following object
{
"listA": [
{
"id": "12345",
"code": "001"
}
]
"listB": [
{
"id": "12345",
"prop": "AABBCC"
}
]
}
In reality my two lists are longer, but the id isn't repeated within each list.
How may I combine the two lists into a single list where each item is an object with the non-id properties for the given id are collected into a single object?
For example, from the object above, I'd like the following:
{
"listC" : [
{
"id": "12345",
"code": "001",
"prop": "AABBCC"
}
]
}
A simple way would be to concatenate the arrays, group the elements by id and map each group into a single object using add;
jq '.listA+.listB | group_by(.id) | map(add)' test.json
If there may be more than two arrays you need to merge in the file, you could instead use flatten to concatenate all of them.
Test case below
# cat test.json
{
"listA": [
{ "id": "12345", "code": "001" },
{ "id": "12346", "code": "002" }
],
"listB": [
{ "id": "12345", "prop": "AABBCC" }
]
}
# jq 'flatten | group_by(.id) | map(add)' test.json
# or
# jq '.listA+.listB | group_by(.id) | map(add)' test.json
[
{
"id": "12345",
"code": "001",
"prop": "AABBCC"
},
{
"id": "12346",
"code": "002"
}
]
Using group_by entails a sort, which is unnecessary, so if efficiency is a concern, then an alternative approach such as the following should be considered:
INDEX(.listA[]; .id) as $one
| INDEX(.listB[]; .id) as $two
| reduce ($one|keys_unsorted[]) as $k ($two; .[$k] += $one[$k])
| {listC: [.[]] }

Resources