How to select items in JQ based on values in array - json

I have a json file with data like this:
{
"data": {
"all": {
"members": [
{
"id": 10,
"name": "First"
},
{
"id": 12,
"name": "Second"
},
{
"id": 14,
"name": "Third"
}
],
"live": {
"online": [
10,
14
]
}
}
}
}
How can I use jq to select and show only the JSON values in data.all.members that have their id in data.all.live.online array?
So the output would be something like:
{
"members": [
{
"id": 10,
"name": "First"
},
{
"id": 14,
"name": "Third"
}
]
}

One way:
jq '.data.all
| .live.online as $online
| {members}
| .members |= map( select(.id | IN($online[]) // null) )
' data.json

Here's a solution that should be fairly easy to understand:
.data.all
| .live.online as $online
| { members: .members | map(select([.id] | inside($online))) }
Output:
{
"members": [
{
"id": 10,
"name": "First"
},
{
"id": 14,
"name": "Third"
}
]
}
And if you need more flexibility how your ids are matched:
.data.all
| .live.online as $online
| { members: .members | map(select(.id as $id | $online | any(. == $id))) }

Related

JQ reshape nested array

I have an issue with jq and nested arrays, I cannot get why it is creating multiple objects:
echo '{
"first": [
{
"second": {
"id": 1,
"third": [
{
"value": "aa"
},
{
"value": "bb"
}
]
}
}
]
}' | jq '.first[].second | {id: .id, prop: .third[].value}'
This is returning:
{
"id": 1,
"prop": "aa"
}
{
"id": 1,
"prop": "bb"
}
But I would like to have:
{
"id": 1,
"prop": ["aa", "bb"]
}
What am I missing?
Use the map builtin to transform the array into just .values:
jq '.first[].second | {id: .id, prop: .third | map(.value)}'
{
"id": 1,
"prop": [
"aa",
"bb"
]
}
Demo
You need to put values in an array :
jq '.first[].second | {id: .id, prop: [.third[].value]}'

need to extract specific string with JQ

I have a JSON file (see below) and with JQ I need to extract the resourceName value for value = mail#mail1.com
So in my case, the result should be name_1
Any idea to do that ?
Because this does not work :
jq '.connections[] | select(.emailAddresses.value | test("mail#mail1.com"; "i")) | .resourceName' file.json
{
"connections": [
{
"resourceName": "name_1",
"etag": "123456789",
"emailAddresses": [
{
"metadata": {
"primary": true,
"source": {
"type": "CONTACT",
"id": "123456"
}
},
"value": "mail#mail1.com",
}
]
},
{
"resourceName": "name_2",
"etag": "987654321",
"emailAddresses": [
{
"metadata": {
"primary": true,
"source": {
"type": "CONTACT",
"id": "654321"
},
"sourcePrimary": true
},
"value": "mail#mail2.com"
}
]
}
],
"totalPeople": 187,
"totalItems": 187
}
One solution is to store the parent object while selecting on the child array:
jq '.connections[] | . as $parent | .emailAddresses // empty | .[] | select(.value == "mail#mail1.com") | $parent.resourceName' file.json
emailAddresses is an array. Use any if finding one element that matches will suffice.
.connections[] | select(any(.emailAddresses[];.value == "mail#mail1.com")).resourceName

Storing top level JSON key for use after reshaping JSON with jq

I'm playing around with some MTGJSON data and I'm trying to convert data from a file called AllPrintings.json that looks like:
{
"10E": {
"cards": [
{
"name": "Abundance",
"prices": {
"paper": {
"2020-06-11": 1.4
},
"paperFoil": {
"2020-06-11": 31.12
}
},
"uuid": "1669af17-d287-5094-b005-4b143441442f"
},
{
"name": "Academy Researchers",
"prices": {
"paper": {
"2020-06-11": 0.36
},
"paperFoil": {
"2020-06-11": 1.22
}
},
"uuid": "047d5499-a21c-5f5c-9679-1599fcaf9815"
}
]
},
"BFZ": {
"cards": [
{
"name": "Adverse Conditions",
"prices": {
"paper": {
"2020-06-11": 0.23
},
"paperFoil": {
"2020-06-11": 1.86
}
},
"uuid": "1669af17-d287-5094-b005-4b143441123"
},
{
"name": "Akoum Firebird",
"prices": {
"paper": {
"2020-06-11": 0.51
},
"paperFoil": {
"2020-06-11": 3.85
}
},
"uuid": "047d5499-a21c-5f5c-9679-1599fcafad567"
}
]
}
}
Into:
{
{
"name": "Abundance",
"price": 1.4,
"uuid": "1669af17-d287-5094-b005-4b143441442f",
"set": "10E"
},
{
"name": "Academy Researchers",
"price": 0.36,
"uuid": "047d5499-a21c-5f5c-9679-1599fcaf9815",
"set": "10E"
},
{
"name": "Adverse Conditions",
"price": 0.23,
"uuid": "1669af17-d287-5094-b005-4b143441123",
"set": "BFZ"
},
{
"name": "Akoum Firebird",
"price": 0.51,
"uuid": "047d5499-a21c-5f5c-9679-1599fcafad567",
"set": "BFZ"
},
}
I'm able to get everything except the set by running
cat AllPrintings.json | jq '[.[] | .cards | .[] | {uuid: .uuid, name: .name, price: .prices.paper | .[]? }]'
which returns
{
{
"name": "Abundance",
"price": 1.4,
"uuid": "1669af17-d287-5094-b005-4b143441442f",
},
{
"name": "Academy Researchers",
"price": 0.36,
"uuid": "047d5499-a21c-5f5c-9679-1599fcaf9815",
},
{
"name": "Adverse Conditions",
"price": 0.23,
"uuid": "1669af17-d287-5094-b005-4b143441123",
},
{
"name": "Akoum Firebird",
"price": 0.51,
"uuid": "047d5499-a21c-5f5c-9679-1599fcafad567",
},
}
I've tried storing the top level keys as $k and can get an array of the keys in a separate command but I'm unable to keep iterating over the original data afterwards. I've tried the comma separator but I get errors or the query hangs. For instance
cat AllPrintings.json | jq ‘. | keys as $k, [.[] | .cards | .[] | {uuid: .uuid, name: .name, price: .prices.paper, key: $k | .[]? }]'
I've search here and have read through the jq documentation but I'm not sure exactly what I'm looking for. I'm also likely overthinking this or missing an obvious solution. Any help would be appreciated. If this is a duplicate question please link me to the original and I'll delete my post.
Thanks.
You were close. The missing part is to_entries, which converts an object into {key, value} pairs:
to_entries | map(.key as $set | .value.cards[] | {uuid, name, price: .prices.paper | .[]?, set: $set })

Processing JSON with jq - handling array index/name into output

I'm trying to use jq to parse a JSON file for me. I want to get a value from a definition header into the output data in place of an index. A simplified example:
{
"header": {
"type": {
"0": {
"name": "Cats"
},
"3": {
"name": "Dogs"
}
}
},
"data": [
{
"time": "2019-01-01T02:00:00Z",
"reading": {
"0": {"value": 90, "note": "start" },
"3": {"value": 100 }
}
}
]
}
Using a jq command like jq '.data[] | {time: .time, data: .reading[]}' gives me:
"time": "2019-01-01T02:00:00Z",
"data": {
"value": 90,
"note": "start"
}
}
{
"time": "2019-01-01T02:00:00Z",
"data": {
"value": 100
}
}
I need to get "Cats" or "Dogs" into the result, heading towards an SQL insert.
Something like:
{
"time": "2019-01-01T02:00:00Z",
"data": {
"type: "Cats", <- line added
"value": 90,
"note": "start"
}
}
...
Or better yet:
{
"time": "2019-01-01T02:00:00Z",
"Cats": { <- label set to "Cats" instead of "data"
"value": 90,
"note": "start"
}
}
...
Is there a way I can get - what I see as the array index "0" or "3" - to be added as "Cats" or "Dogs"?
Using the built-in function, INDEX, for creating a dictionary allows a straightforward solution as follows:
(.header.type
| INDEX(to_entries[]; .key)
| map_values(.value.name)) as $dict
| .data[]
| (.reading | keys_unsorted[]) as $k
| {time} + { ($dict[$k]) : .reading[$k] }
Output
{
"time": "2019-01-01T02:00:00Z",
"Cats": {
"value": 90,
"note": "start"
}
}
{
"time": "2019-01-01T02:00:00Z",
"Dogs": {
"value": 100
}
}

Selecting in JQ with Contains in a Array

I want to select the particular item from the array using contains and get the first item using JQ.
JQ:
.amazon.items[] | select(.name | contains ("shoes"))
JSON:
{
"amazon": {
"activeitem": 2,
"items": [
{
"id": 1,
"name": "harry potter",
"state": "sold"
},
{
"id": 2,
"name": "adidas shoes",
"state": "in inventory"
},
{
"id": 3,
"name": "watch",
"state": "returned"
},{
"id": 4,
"name": "adidas shoes",
"state": "in inventory"
}
]
}
}
Expected Result:
{
"activeitem": 2,
"item": {
"id": 2,
"name": "adidas shoes",
"state": "in inventory"
}
}
Actual :
Tried various options like but not getting the Intended response .
.amazon.items[] | select(.name | contains ("shoes"))
.amazon.items | select(.[].name | contains ("shoes")) | .[0]
Also when I try to combine activeitem and item, I get something like this, which is also wrong.
{
"activeitem": 2,
"item": {
"id": 2,
"name": "adidas shoes",
"state": "in inventory"
}
},
{
"activeitem": 2,
"item": {
"id": 2,
"name": "adidas shoes",
"state": "in inventory"
}
}
To edit "in-place" you could write:
.amazon
| .items |= map(select(.name | contains ("shoes")))[0]
If you really want to change the name 'items' to 'item', you could tweak the above as follows:
.amazon
| .item = (.items | map(select(.name | contains ("shoes")))[0])
| del(.items)