jq get first value by priority and condition - json

I have following json:
{
"Detail": {
"Response": [
{
"ID": "8000000D-1483989576",
"Name": "",
"FullName": "FullName 1"
},
{
"ID": "8000000C-1483985849",
"Name": "Name 1"
},
{
"ID": "80000006-1481277410",
"Name": "Name 2",
"FullName": "FullName 2"
},
{
"ID": "8000000B-1481537384",
"Name": "Name 3"
}
]
}
}
I'm trying to create another json that will consider the non-empty/not null .Name as priority otherwise get .FullName regardless if it's empty or null, the final json would look like following:
[
{
"id": "8000000D-1483989576",
"name": "FullName 1"
},
{
"id": "8000000C-1483985849",
"name": "Name 1"
},
{
"id": "80000006-1481277410",
"name": "FullName 2"
},
{
"id": "8000000B-1481537384",
"name": "Name 3"
}
]
The temporary solution I got is to use join
jq '[.Detail.Response[] | {id: .ID, name: [.Name, .FullName] | join("") }]'
But of course, it'll only work if .FullName is empty or null.

This should get you on your way:
.Detail.Response[]
| { id: .ID, Name: (if .Name != "" then .Name else .FullName end) }

I figure out a way to do it using map and select.
jq '[.Detail.Response[] | {id: .ID, name: [.Name, .FullName] | map(select(length > 0)) | first }]'

Related

Using jq to search for a value based on a key located deep in json file

I am new to jq and I'm trying to use it to search for a value in a json file based on a key that is located deep in the json structure. Here is a sample of my json file:
{
"data": {
"inventory": {
"location": "remote",
"list": {
"content": [
{
"item": {
"name": "minivan"
},
"owner": {
"id": "12345",
"state": "CA"
}
},
{
"item": {
"name": "sedan"
},
"owner": {
"id": "67890",
"state": "AZ"
}
}
]
}
}
}
}
An example of search that I'm trying to do is:
select item.name where owner.id = "67890"
and the expected output would be:
item.name = "sedan"
I'm trying to run the following:
jq '.[] | select .owner.id = "67890" | .item.name' json
and it generates an error:
jq: error: select/0 is not defined at <top-level>, line 1:
.[] | select .owner.id = "67890" | .item.name
jq: 1 compile error
Any pointers on how to do this in jq would be much appreciated!
Thanks!
First, you have to "navigate" to where you want to make the query. This seems to be an array.
.data.inventory.list.content
[
{
"item": {
"name": "minivan"
},
"owner": {
"id": "12345",
"state": "CA"
}
},
{
"item": {
"name": "sedan"
},
"owner": {
"id": "67890",
"state": "AZ"
}
}
]
Demo
Next, let's iterate over that array's items, which gives us a stream of objects.
.[]
{
"item": {
"name": "minivan"
},
"owner": {
"id": "12345",
"state": "CA"
}
}
{
"item": {
"name": "sedan"
},
"owner": {
"id": "67890",
"state": "AZ"
}
}
Demo
From these objects we select those that match your criteria.
select(.owner.id == "67890")
{
"item": {
"name": "sedan"
},
"owner": {
"id": "67890",
"state": "AZ"
}
}
Demo
Finally, we extract the value you're interested in.
.item.name
"sedan"
Demo
Everything combined in a jq call would be:
jq '.data.inventory.list.content[] | select(.owner.id == "67890").item.name'
"sedan"
Demo
This output is still valid JSON document (containing nothing but a JSON string). If you want to process the output as raw text, use the --raw-output (or -r) option:
jq -r '.data.inventory.list.content[] | select(.owner.id == "67890").item.name'
sedan
Demo
Here's a solution that avoids having to "navigate" to the right place, and which is also quite close to your SQL-like query:
..
| objects
| select(.owner and
(.owner|type=="object" and .id == "67890"))
.item.name
or more succinctly:
..|objects|select(.owner.id? == "67890").item.name

How to filter an list by the value of an inner dict with jq?

Giving the input JSON:
[
{
"name": "foo",
"value": 1
},
{
"name": "bar",
"value": 1
},
{
"name": "foo",
"value": 2
}
]
I'm trying to get the dicts with the name foo, so the expecting output is:
{
"name": "foo",
"value": 1
},
{
"name": "foo",
"value": 2
}
Try this
jq '.[] | select(.name == "foo")'
Demo

Map conditional child elements

I am working with a JSON file which has contains lot of data that can be removed before sending to an API.
Found that JQ can be used to achieve this but not sure on how to map to get the desired results.
Input JSON
{
"name": "Sample name",
"id": "123",
"userStory": {
"id": "234",
"storyName": "Story Name",
"narrative": "Narrative",
"type": "feature"
},
"testSteps": [
{
"number": 1,
"description": "Step 1",
"level": 0,
"children": [
{
"number": 2,
"description": "Description",
"children": [
{
"number": 3,
"description": "Description"
}
]
},
{
"number": 4,
"anotherfield": "another field"
}
]
}
]
}
Desired Output
{
"name": "Sample name",
"userStory": {
"storyName": "Story Name"
},
"testSteps": [
{
"description": "Step 1",
"children": [
{
"description": "Description",
"children": [
{
"description": "Description"
}
]
},
{
"anotherfield": "anotherfield"
}
]
}
]
}
Tried to do it with the following jq command
map_values(..|{name, id, userStory})
but not sure how to filter only the userStory.storyName.
Thanks in advance.
Note: The actual JSON has different child elements that are repeated in some cases.
To delete .id from root object:
del(.id)
To leave only .storyName in .userStory:
.userStory |= {storyName}
To delete .number and .level from every object on any level in .testSteps:
.testSteps |= walk(if type == "object" then del(.number, .level) else . end)
Putting it all together:
del(.id) | (.userStory |= {storyName}) | (.testSteps |=
walk(if type == "object" then del(.number, .level) else . end))
Online demo

Is there a way to use default in object construction with jq?

I want to filter and assign a value from array based on a condition, and use default in case if array does not have the matched object.
Here is a sample object:
{
"array" : [
{
"id": "A",
"conversations": [
{
"conversation": "1",
"type": "good"
},
{
"conversation": "2",
"type": "bad"
}
]
},
{
"id": "B",
"conversations": [
{
"conversation": "3",
"type": "good"
},
{
"conversation": "4",
"type": "bad"
}
]
},
{
"id": "C",
"conversations": [
{
"conversation": "5",
"type": "bad"
},
{
"conversation": "6",
"type": "bad"
}
]
}
]
}
Required output:
{
"id": "A",
"goodConversation": "1"
}
{
"id": "B",
"goodConversation": "3"
},
{
"id": "C",
"goodConversation": null
}
echo of my input:
echo '{"array":[{"id":"A","conversations":[{"conversation":"1","type":"good"},{"conversation":"2","type":"bad"}]},{"id":"B","conversations":[{"conversation":"3","type":"good"},{"conversation":"4","type":"bad"}]},{"id":"C","conversations":[{"conversation":"5","type":"bad"},{"conversation":"6","type":"bad"}]}]}'
I tried running following jq
jq '.array[] | {id, "goodConversation": .conversations[] | select(.type == "good") | .conversation}'
Actual output:
{
"id": "A",
"goodConversation": "1"
}
{
"id": "B",
"goodConversation": "3"
}
since the object with id: "C" does not have any good conversation the whole object gets filtered out. Is there a way to create the output object which contains "C" with null as value?
Clarification:
"conversations" will have at most one good conversation.
I am using jq 1.5
One way to provide a default value is often to use the // "alternative" operator. Building on the foundations you've laid, you could write:
.array[]
| {id,
"goodConversation":
((.conversations[]
| select(.type == "good")
| .conversation) // null) }
If there is more than one "good" conversation, however, this may not be exactly what you want. If it's not, then consider using first, e.g.:
.array[]
| {id,
"goodConversation":
( first(.conversations[]
| select(.type == "good")
| .conversation) // null)}

Extract keys and data and write an array

Given the following json source
{
"pages":{
"yomama/first key": {
"data": {
"fieldset": "lesson-video-overview",
"title": "5th Grade Math - Interpreting Fractions",
},
"order": 4
},
"yomama/second key": {
"data": {
"fieldset": "lesson-video-clip-single",
"title": "Post-Lesson Debrief Part 5",
},
"order": 14
},
"yopapa/Third key": {
"data": {
"fieldset": "lesson-video-clip-single",
"title": "Lesson Part 2B",
},
"order": 6
}
}
}
How could I output an array-type output like this? The main challenge for me is extracting the key e.g. "yomama/first key" and in the ideal world, I can filter like just give me an array of those keys that start with "yomama" (but not yopapa)
[
{
"url" : "yomama/first key",
"data": {
"fieldset": "lesson-video-overview",
"title": "5th Grade Math - Interpreting Fractions",
},
"order": 4
},
{
"url" : "yomama/second key",
"data": {
"fieldset": "lesson-video-clip-single",
"title": "Post-Lesson Debrief Part 5",
},
"order": 14
},
{
"url" : "yopapa/Third key",
"data": {
"fieldset": "lesson-video-clip-single",
"title": "Lesson Part 2B",
},
"order": 6
}
]
Assuming the input is in so.json and corrected to well-formatted JSON you may use:
jq '[.pages | to_entries[] | {"url": .key, "data": .value.data, "order": .value.order}]' < so.json
Here's a solution that does not require being explicit about including all the other keys:
.pages
| [ to_entries[]
| select(.key | startswith("yomama"))
| {url: .key} + .value ]