jq - Copy values of first array element if other values are null - json

I have an object which contains an array of objects. I want to replace null values of a specific key value pair with the value of the 0th array index. In the example below I want for array elements 1 to contain "website" and "email" of the "email" and "website" of the 0th array element because they are both null. For element 2 I only expect the "website" to be set with the result of the 0th array element as only that is null.
The json that I have is
{
"id": 123,
"offices": [
{
"officeId": 12345,
"name": "Name LLP",
"website": "www.example.com",
"email": "website#example.com",
"officeType": "HO"
},
{
"officeId": 123456,
"name": "Name",
"website": null,
"email": null,
"officeType": "BRANCH"
},
{
"officeId": 1234567,
"name": "Name",
"website": null,
"email": "example#website.com",
"officeType": "BRANCH"
},
],
}
My expected json output would be
{
"id": 123,
"offices": [
{
"officeId": 12345,
"name": "Name LLP",
"website": "www.example.com",
"email": "website#example.com",
"officeType": "HO"
},
{
"officeId": 123456,
"name": "Name",
"website": "www.example.com",
"email": "website#example.com",
"officeType": "BRANCH"
},
{
"officeId": 1234567,
"name": "Name",
"website": "www.example.com",
"email": "example#website.com",
"officeType": "BRANCH"
},
],
}
I have attempted to solve this using map and walk but cannot seem to find the correct way to solve it

After fixing the errors in your JSON file so it's valid:
jq '. as $orig |
.offices |= map(.website //= $orig.offices[0].website |
.email //= $orig.offices[0].email)' input.json
{
"id": 123,
"offices": [
{
"officeId": 12345,
"name": "Name LLP",
"website": "www.example.com",
"email": "website#example.com",
"officeType": "HO"
},
{
"officeId": 123456,
"name": "Name",
"website": "www.example.com",
"email": "website#example.com",
"officeType": "BRANCH"
},
{
"officeId": 1234567,
"name": "Name",
"website": "www.example.com",
"email": "example#website.com",
"officeType": "BRANCH"
}
]
}

Since you have not given any details about your attempts, let me, in the spirit of "How to Solve It", suggest a strategy for doing so.
Specifically, let's formulate and solve the obvious "subproblem" which should make the solution to the original problem easy to the point of almost being trivial. The obvious "subproblem" is: given a reference object, $ref, how to update another object so that null-valued keys in the latter are taken from $ref if available?
def infer($ref):
with_entries( if .value == null then $ref[.key] else . end);
Now the original problem becomes much easier, right?

Related

How to extract a paticular key from the json

I am trying to extract values from a json that I obtained using the curl command for api testing. My json looks as below. I need some help extracting the value "20456" from here?
{
"meta": {
"status": "OK",
"timestamp": "2022-09-16T14:45:55.076+0000"
},
"links": {},
"data": {
"id": 24843,
"username": "abcd",
"firstName": "abc",
"lastName": "xyz",
"email": "abc#abc.com",
"phone": "",
"title": "",
"location": "",
"licenseType": "FLOATING",
"active": true,
"uid": "u24843",
"type": "users"
}
}
{
"meta": {
"status": "OK",
"timestamp": "2022-09-16T14:45:55.282+0000",
"pageInfo": {
"startIndex": 0,
"resultCount": 1,
"totalResults": 1
}
},
"links": {
"data.createdBy": {
"type": "users",
"href": "https://abc#abc.com/rest/v1/users/{data.createdBy}"
},
"data.fields.user1": {
"type": "users",
"href": "https://abc#abc.com/rest/v1/users/{data.fields.user1}"
},
"data.modifiedBy": {
"type": "users",
"href": "https://abc#abc.com/rest/v1/users/{data.modifiedBy}"
},
"data.fields.projectManager": {
"type": "users",
"href": "https://abc#abc.com/rest/v1/users/{data.fields.projectManager}"
},
"data.parent": {
"type": "projects",
"href": "https://abc#abc.com/rest/v1/projects/{data.parent}"
}
},
"data": [
{
"id": 20456,
"projectKey": "Stratus",
"parent": 20303,
"isFolder": false,
"createdDate": "2018-03-12T23:46:59.000+0000",
"modifiedDate": "2020-04-28T22:14:35.000+0000",
"createdBy": 18994,
"modifiedBy": 18865,
"fields": {
"projectManager": 18373,
"user1": 18628,
"projectKey": "Stratus",
"text1": "",
"name": "Stratus",
"description": "",
"date2": "2019-03-12",
"date1": "2018-03-12"
},
"type": "projects"
}
]
}
I have tried the following, but end up getting error:
▶ cat jqTrial.txt | jq '.data[].id'
jq: error (at <stdin>:21): Cannot index number with string "id"
20456
Also tried this but I get strings outside the object that I am not sure how to remove:
cat jqTrial.txt | jq '.data[]'
Assuming you want the project id not the user id:
jq '
.data
| if type == "object" then . else .[] end
| select(.type == "projects")
| .id
' file.json
There's probably a better way to write the 2nd expression
Indeed, thanks to #pmf
.data | objects // arrays[] | select(.type == "projects").id
Your input consists of two JSON documents; both have a data field on top level. But while the first one is itself an object which has an .id field, the second one is an array with one object item, which also has an .id field.
To retrieve both, you could use the --slurp (or -s) option which wraps both top-level objects into an array, then you can address them separately by index:
jq --slurp '.[0].data.id, .[1].data[].id' jqTrial.txt
24843
20456
Demo

jq get unique value from two keys

i know to get a unique from one key - unique_by('.[].name)
i want to get output by checking for unique values in two keys
but how to do for two keys like unique_by('.[].name,.[].url') and return the input along with other keys?
#input
[
{
"name": "abc",
"url": "https://aa.com",
"created_at": "2022-09-30T11:17:33.181Z"
},
{
"name": "bb",
"url": "https://ddd.com",
"created_at": "2022-09-30T11:14:33.180Z"
},
{
"name": "abc",
"url": "https://aa.com",
"created_at": "2022-09-30T11:14:33.180Z"
}
]
#expected output
[
{
"name": "abc",
"url": "https://aa.com",
"created_at": "2022-09-30T11:17:33.181Z"
},
{
"name": "bb",
"url": "https://ddd.com",
"created_at": "2022-09-30T11:14:33.180Z"
}
]
Collect the criteria into an array:
unique_by([.name, .url])
Just provide to unique_by an array with everything included, so that the array must become unique:
jq 'unique_by([.name, .url])'
[
{
"name": "abc",
"url": "https://aa.com",
"created_at": "2022-09-30T11:17:33.181Z"
},
{
"name": "bb",
"url": "https://ddd.com",
"created_at": "2022-09-30T11:14:33.180Z"
}
]
Demo

Need to find key-value pair and replace key-value pair in JSON using JQ

I have this JSON
{
"firstName": "Rajesh",
"lastName": "Kumar",
"gender": "man",
"age": 24,
"address": {
"streetAddress": "126 Udhna",
"city": "Surat",
"state": "WB",
"postalCode": "394221"
},
"phoneNumbers": [
{
"type": "home",
"number": "7383627627"
}
]
}
I need to find the value of the "state" key Using JQ and replace the value in JSON. I do not want to fetch it by providing the position of the key, Like
firstName=$(cat sample-json.json | jq -r '.firstName')
My expected output
{
"firstName": "Rajesh",
"lastName": "Kumar",
"gender": "man",
"age": 24,
"address": {
"streetAddress": "126 Udhna",
"city": "Surat",
"state": "Bihar",
"postalCode": "394221"
},
"phoneNumbers": [
{
"type": "home",
"number": "7383627627"
}
]
}
If you're willing to specify .address:
jq '.address.state = "Bihar"' sample-json.json
Otherwise:
jq 'walk(if type == "object" and has("state") then .state = "Bihar" else . end)' sample-json.json
This last will replace all .state values. If you only want to replace the first occurrence:
jq 'first(..|objects|select(has("state"))).state = "Bihar"' sample-json.json
And so on. It would really help all concerned if you could make the requirements clear.

Find a record in json Object if the record has specific key in python

I have a JSON object which has 100000 records. I want a select a record which has specific value to the one of the key
Eg:
[{
"name": "bindu",
"age": "24",
"qualification": "b.tech"
},
{
"name": "naveen",
"age": "23",
"qualification": "b.tech"
},
{
"name": "parvathi",
"age": "23",
"qualification": "m.tech"
},
{
"name": "bindu s",
"status": "married"
},
{
"name": "naveen k",
"status": "unmarried"
}]
now I want to combine the records which are having the name with 'bindu' and 'bindu s. We can achieve this by iterating on the JSON object but since the size is more it is taking more time. Is there any way to make this easy.
I want the output like
[{
"name": "bindu",
"age": "24",
"qualification": "b.tech",
"status": "married"
},
{
"name": "naveen",
"age": "23",
"qualification": "b.tech",
"status": "unmarried"
},
{
"name": "parvathi",
"age": "23",
"qualification": "m.tech"
"status": ""
},
This will rename and merge your objects by first name.
jq 'map(.name |= split(" ")[0]) | group_by(.name) | map(add)'

Parsing Git Json with Regular Express

I am taking a Github json file and parsing it with Java's regular expression library JsonPath. I am having a problem parsing arrays that do not have labels.
I need to send a email every time a particular file is changed in our repository.
Here is the Git Json:
{
"trigger": "push",
"payload": {
"type": "GitPush",
"before": "xxxxxxxx",
"after": "yyyyyyyy",
"branch": "branch-name",
"ref": "refs/heads/branch-name",
"repository": {
"id": 42,
"name": "repo",
"title": "repo",
"type": "GitRepository"
},
"beanstalk_user": {
"type": "Owner",
"id": 42,
"login": "username",
"email": "user#example.org",
"name": "Name Surname"
},
"commits": [
{
"type": "GitCommit",
"id": "ffffffff",
"message": "Important changes.",
"branch": "branch-name",
"author": {
"name": "Name Surname",
"email": "user#example.org"
},
"beanstalk_user": {
"type": "Owner",
"id": 42,
"login": "username",
"email": "user#example.org",
"name": "Name Surname"
},
"changed_files": {
"added": [
"NEWFILE",
],
"deleted": [
"Gemfile",
"NEWFILE"
],
"modified": [
"README.md",
"NEWFILE"
],
"copied": [
]
},
"changeset_url": "https://subdomain.github.com/repository-name/changesets/ffffffff",
"committed_at": "2014/08/18 13:30:29 +0000",
"parents": [
"afafafaf"
]
}
]
}
}
This is the expression I am using: to get the commits
$..changed_files
This return the whole changed files part but I can not explicitly choose the name "NEWFILE"
I tried
$..changed_files.*[?(#.added == "NEWFILE")]
$..changed_files.*[?(#.*== "NEWFILE")]
It just returns a empty array.
I just want it to return Newfile and what type of change. Any Ideas?
You can use the following JsonPath to retrieve the commits which list "NEWFILE" as an added file :
$.payload.commits[?(#.changed_files.added.indexOf("NEWFILE") != -1)]