Select random id from list in jq and update value - arrays

I have some sample payload that I am going to be receiving, it looks like this:
[
{
"Id": "9",
"Line": [
{
"Amount": 100,
"Description": "Weekly Gardening Service",
"DetailType": "SalesItemLineDetail",
"Id": "1",
"LineNum": 1,
"SalesItemLineDetail": {
"ItemAccountRef": {
"name": "Landscaping Services",
"value": "45"
},
"ItemRef": {
"name": "Gardening",
"value": "6"
},
"Qty": 4,
"TaxCodeRef": {
"value": "TAX"
},
"UnitPrice": 25
}
},
{
"Amount": 100,
"DetailType": "SubTotalLineDetail",
"SubTotalLineDetail": {}
}
]
},
{
"Id": "10",
"Line": [
{
"Amount": 140,
"Description": "Weekly Gardening Service",
"DetailType": "SalesItemLineDetail",
"Id": "1",
"LineNum": 1,
"SalesItemLineDetail": {
"ItemAccountRef": {
"name": "Landscaping Services",
"value": "45"
},
"ItemRef": {
"name": "Gardening",
"value": "6"
},
"Qty": 4,
"TaxCodeRef": {
"value": "NON"
},
"UnitPrice": 35
}
},
{
"Amount": 35,
"Description": "Pest Control Services",
"DetailType": "SalesItemLineDetail",
"Id": "2",
"LineNum": 2,
"SalesItemLineDetail": {
"ItemAccountRef": {
"name": "Pest Control Services",
"value": "54"
},
"ItemRef": {
"name": "Pest Control",
"value": "10"
},
"Qty": 1,
"TaxCodeRef": {
"value": "NON"
},
"UnitPrice": 35
}
},
{
"Amount": 175,
"DetailType": "SubTotalLineDetail",
"SubTotalLineDetail": {}
}
]
}
]
These I know are valid and I need to cross reference them, by id, in another payload I am receiving. But, the data I am receiving I can't assume to have valid ID's.
So, I want to take all the valid Ids from above, and shove them, randomly, into the sample data I have, that looks like this ($.invoices[].qbId):
[
{
"id": "fb2430c5-5970-46b0-9947-aaa0b9f177bb",
"invoices": [
{
"description": "2022-02-03 - 179",
"dueDate": "2022-02-03T22:51:10.206Z",
"id": "6f904b18-71c6-4fec-a016-7452f6a6b1dc",
"invoiceDate": "2022-02-03T22:51:10.347Z",
"openBalance": 200,
"paidAmount": 200,
"qbId": "1",
"totalAmount": 212
}
]
},
{
"id": "fa5b77b5-bfd4-4178-ac31-386ec83f530c",
"invoices": [
{
"description": "2022-01-12 - 95",
"dueDate": "2022-01-12T14:08:26.219Z",
"id": "05a58be3-4396-4c15-b9c2-ece68cb2b3fb",
"invoiceDate": "2022-01-12T14:08:26.399Z",
"openBalance": 7.33,
"paidAmount": 7.33,
"qbId": "",
"totalAmount": 7.33
},
{
"description": "2022-01-12 - 95",
"dueDate": "2022-01-12T14:08:26.219Z",
"id": "91f5ecd0-e18d-4029-8745-143323e02007",
"invoiceDate": "2022-01-12T14:08:26.580Z",
"openBalance": 53.13,
"paidAmount": 53.13,
"qbId": "",
"totalAmount": 53.13
}
]
}
]
this jq will get me my ids jq '.QueryResponse.Invoice | map(.Id)' which can be readily consumed by jq. The question now is (and this is what I don't know) how to randomly choose from this array and update the sample payload:
jq 'map(. + {
invoices : .invoices | map(. + {qbId: ??random here })
})
'

If I understood correctly, you want to replace each id field (spelling may differ, sometimes it's Id) with a randomly generated id string.
This solution first extracts the paths of all such id fields (in various spellings) using jq, then iterates over the result in the shell, using uuidgen to generate an id for each, which is fed into another jq call which uses setpath to change the value at the paths saved to the ids generated:
file="input.json"
jq -c '
paths(.. | scalars) | select(.[-1] == ("id", "Id", "ID")) | tojson
' "$file" |
while read -r json; do printf '["%s",%s]\n' "$(uuidgen)" "$json"; done |
jq -n --argfile file "$file" '
reduce inputs as [$id,$json] ($file; setpath($json | fromjson; $id))
'
[
{
"Id": "10162eb7-29ba-4b60-ad20-e5b1133eca63",
"Line": [
{
"Amount": 100,
"Description": "Weekly Gardening Service",
"DetailType": "SalesItemLineDetail",
"Id": "272832df-a8f5-4877-92de-1545150afc33",
"LineNum": 1,
"SalesItemLineDetail": {
"ItemAccountRef": {
"name": "Landscaping Services",
"value": "45"
},
"ItemRef": {
"name": "Gardening",
"value": "6"
},
"Qty": 4,
"TaxCodeRef": {
"value": "TAX"
},
"UnitPrice": 25
}
},
{
"Amount": 100,
"DetailType": "SubTotalLineDetail",
"SubTotalLineDetail": {}
}
]
},
{
"Id": "190b0e50-e007-46a4-b1ca-c3efb762629c",
"Line": [
{
"Amount": 140,
"Description": "Weekly Gardening Service",
"DetailType": "SalesItemLineDetail",
"Id": "f7067227-56d4-4849-873a-3ee5c336999e",
"LineNum": 1,
"SalesItemLineDetail": {
"ItemAccountRef": {
"name": "Landscaping Services",
"value": "45"
},
"ItemRef": {
"name": "Gardening",
"value": "6"
},
"Qty": 4,
"TaxCodeRef": {
"value": "NON"
},
"UnitPrice": 35
}
},
{
"Amount": 35,
"Description": "Pest Control Services",
"DetailType": "SalesItemLineDetail",
"Id": "181d7c6b-0afa-4f44-a568-2c482fc5c285",
"LineNum": 2,
"SalesItemLineDetail": {
"ItemAccountRef": {
"name": "Pest Control Services",
"value": "54"
},
"ItemRef": {
"name": "Pest Control",
"value": "10"
},
"Qty": 1,
"TaxCodeRef": {
"value": "NON"
},
"UnitPrice": 35
}
},
{
"Amount": 175,
"DetailType": "SubTotalLineDetail",
"SubTotalLineDetail": {}
}
]
}
]

This shows how to select elements at random from an array, assuming a bash or sufficiently bash-like environment:
#!/bin/bash
< /dev/urandom tr -cd '0-9' | fold -w 1 | jq -MRnc '
# Output: a prn in range(0;$n) where $n is `.`
def prn:
if . == 1 then 0
else . as $n
| ([1, (($n-1)|tostring|length)]|max) as $w
| [limit($w; inputs)] | join("") | tonumber
| if . < $n then . else ($n | prn) end
end;
# Input: an array
# Output: an array, being a selection of $k elements from . chosen at random without replacement
def prns($k):
if $k <= 0 then []
else . as $in
| length as $n
| if $k > $n then "no can do" | error
else ($n|prn) as $ix
| [$in[$ix]] + (($in[0:$ix] + $in[$ix+1:])|prns($k-1))
end
end;
# Two illustrations
# Three from range(0,10) (with replacement):
[range(0;10) | ( ["a", "b", "c"] | .[length|prn]) ],
# Three from an array, without replacement:
([range(0;10)] | prns(3))
'

Related

I'm new to this, What am I doing wrong? Creating array of line items in JSON and getting parse error. - Expecting 'STRING', got '{'

enter image description here
Parse error on line 15:
...,
"Qty": 3
},
{
"Amount":
--------------------^
Expecting 'STRING', got '{'
I don't know how to format the code for stack overflow but this is the JSON array I'm trying to put together
{
"MetaData": {
"CreateTime": "2019-05-16T18:13:13-08:00",
"LastUpdatedTime": "2019-05-16T18:13:45-08:00"
},
"Line": [{
"Amount": 135,
"DetailType": "SalesItemLineDetail",
"SalesItemLineDetail": {
"ItemRef": {
"value": "1",
"name": "LV"
},
"Qty": 3
},
{
"Amount": 135,
"DetailType": "SalesItemLineDetail",
"SalesItemLineDetail": {
"ItemRef": {
"value": "1",
"name": "LV"
},
"Qty": 3
} }
],
"CustomerRef": {
"value": "20"
}
}
The solution is probably simple
{
"MetaData": {
"CreateTime": "2019-05-16T18:13:13-08:00",
"LastUpdatedTime": "2019-05-16T18:13:45-08:00"
},
"Line": [
{
"Amount": 135,
"DetailType": "SalesItemLineDetail",
"SalesItemLineDetail": {
"ItemRef": {
"value": "1",
"name": "LV"
},
"Qty": 3
} <--- MISSING BRACE HERE
},
{
"Amount": 135,
"DetailType": "SalesItemLineDetail",
"SalesItemLineDetail": {
"ItemRef": {
"value": "1",
"name": "LV"
},
"Qty": 3
}
}
],
"CustomerRef": {
"value": "20"
}
}

how to select value from json file which find another value matches use jq

I have been struggling with json stuff. I want to find running state's href. How can I do that with jq or another like bash-style?
Here is my curl output:
{
"relations": {
"total": 9,
"link": [
{
"href": "https://vro:8281/vco/api/workflows/6433f56f-13b7-46a7-a9ec-a3e38c7ff69d/",
"rel": "up"
},
{
"href": "https://vro:8281/vco/api/workflows/6433f56f-13b7-46a7-a9ec-a3e38c7ff69d/executions/",
"rel": "add"
},
{
"attributes": [
{
"value": "8f961082-cccc-412f-9244-16ba5b949dbe",
"name": "id"
},
{
"value": "2019-09-28T17:20:40.691-01:00",
"name": "startDate"
},
{
"value": "2019-09-28T17:20:43.949-01:00",
"name": "endDate"
},
{
"value": "completed",
"name": "state"
},
{
"value": "test",
"name": "name"
},
{
"name": "currentItemDisplayName"
}
],
"href": "https://vro:8281/vco/api/workflows/6433f56f-13b7-46a7-a9ec-a3e38c7ff69d/executions/8f961082-cccc-412f-9244-16ba5b949dbe/",
"rel": "down"
},
{
"attributes": [
{
"value": "b28832cb-2a97-4ec8-848f-35fec95eb867",
"name": "id"
},
{
"value": "2019-09-28T17:21:04.643-01:00",
"name": "startDate"
},
{
"value": "running",
"name": "state"
},
{
"value": "test",
"name": "name"
},
{
"name": "currentItemDisplayName"
}
],
"href": "https://vro:8281/vco/api/workflows/6433f56f-13b7-46a7-a9ec-a3e38c7ff69d/executions/b28832cb-2a97-4ec8-848f-35fec95eb867/",
"rel": "down"
}
}
If I talk with example I want to find href key is https://vro:8281/vco/api/workflows/6433f56f-13b7-46a7-a9ec-a3e38c7ff69d/executions/b28832cb-2a97-4ec8-848f-35fec95eb867/ by way of running key.
After the sample JSON has been fixed, the following jq query:
.relations.link[]
| select( has("attributes") )
| select( any(.attributes[]; .value=="running" and .name == "state") )
| .href
produces:
"https://vro:8281/vco/api/workflows/6433f56f-13b7-46a7-a9ec-a3e38c7ff69d/executions/b28832cb-2a97-4ec8-848f-35fec95eb867/"
In two lines (i.e., with one pipe)
.relations.link[]
| select(any(.attributes[]?; .value=="running" and .name == "state")).href

Jq to add the element in json array conditionally and print the entire modified file

I have a json file with the below format.
I would like to add the element {"test" : "2"}in propDefs[] if the .children[].type=="environmentApprovalTask" and .children[].role.name== "GCM approver" and output that to the new file. I want the entire file with the modified content.The .children[] array may not have always 3 elements.
{
"edges": [
{
"to": "de32e562319310b7b4fe3736e22009",
"from": "99d5f0b278f08721adba7741b782d8",
"type": "SUCCESS",
"value": ""
},
{
"to": "06916609ad7fd4127815be3f075c81",
"from": "de32e562319310b7b4fe3736e22009",
"type": "SUCCESS",
"value": ""
},
{
"to": "99d5f0b278f08721adba7741b782d8",
"type": "ALWAYS",
"value": ""
}
],
"offsets": [
{
"name": "99d5f0b278f08721adba7741b782d8",
"x": -91,
"y": 100,
"h": 70,
"w": 290
},
{
"name": "06916609ad7fd4127815be3f075c81",
"x": -5,
"y": 420,
"h": 80,
"w": 120
},
{
"name": "de32e562319310b7b4fe3736e22009",
"x": 69,
"y": 240,
"h": 70,
"w": 240
}
],
"layoutMode": "manual",
"type": "graph",
"id": "d5d9c4c4-0c5f-4642-872c-ac892039eaa4",
"name": "79e8b952-dd59-4cfd-9c0c-e6c08a81d4ca",
"children": [
{
"type": "finish",
"id": "833e959c-6825-413d-afc4-7b74c0a87c3e",
"name": "06916609ad7fd4127815be3f075c81",
"children": []
},
{
"id": "e976041d-af9d-48cf-b838-735bb5efd483",
"type": "envApprovalTask",
"children": [],
"name": "de32e562319310b7b4fe3736e22009",
"roleRestrictionData": {
"contextType": "ENVIRONMENT",
"roleRestrictions": [
{
"roleId": "087175fb-5d38-42d1-b65a-2b6a6958bc21"
}
]
},
"propDefs": [{"test": "1"}],
"templateName": "ApprovalCreated",
"commentRequired": false,
"commentPrompt": "",
"role": {
"id": "087175fb-5d38-42d1-b65a-2b6a6958bc21",
"name": "Approver",
"isDeletable": true
}
},
{
"id": "513fbc6a-3c4a-4a10-9eb0-7dc893c69413",
"type": "environmentApprovalTask",
"children": [],
"name": "99d5f0b278f08721adba7741b782d8",
"roleRestrictionData": {
"contextType": "ENVIRONMENT",
"roleRestrictions": [
{
"roleId": "116b8cd5-e7e4-403d-9599-35fe25d3cba2"
}
]
},
"propDefs": [],
"templateName": "ApprovalCreated",
"commentRequired": false,
"commentPrompt": "",
"role": {
"id": "116b8cd5-e7e4-403d-9599-35fe25d3cba2",
"name": "Manager Approver",
"isDeletable": true
}
}
]
}
My final attempt with the code
cat file.json |jq '.|.children[]| select(.type=="envApprovalTask")|select(.role.name=="Approver") |.propDefs[.profDefs|length] |= .+ {"test" : "2"}'
This only produces the modified element, didnt produce the entire file as output. Please help how can i get the desired output.
TIL about complex assignments in jq. This actually works:
(.children[] | select(.type=="envApprovalTask" and .role.name=="Approver") | .propDefs) |= .+[{"test":"2"}]
The only significant difference from your version is the parenthesised left side of the assignment.

How to extract specific object from JSON with arrays via jq

How can I extract only the objects id and name from this JSON via jq?
The output should be look like the format below. This is just an example for the required output, the real one that I need is to catch the whole values id and name like in the JSON source.
This is the required output:
{
"name": "Auto Body Styles",
"id": "1.1"}
{
"name": "Convertible",
"id": "1.1.2"
}
This is the JSON source file:
{
"name": "Automotive",
"id": "1",
"categories": [
{
"name": "Auto Body Styles",
"id": "1.1",
"categories": [
{
"name": "Commercial Trucks",
"id": "1.1.1"
},
{
"name": "Convertible",
"id": "1.1.2"
},
{
"name": "Coupe",
"id": "1.1.3"
},
{
"name": "Crossover",
"id": "1.1.4"
},
{
"name": "Hatchback",
"id": "1.1.5"
},
{
"name": "Microcar",
"id": "1.1.6"
},
{
"name": "Minivan",
"id": "1.1.7"
},
{
"name": "Off-Road Vehicles",
"id": "1.1.8"
},
{
"name": "Pickup Trucks",
"id": "1.1.9"
},
{
"name": "Sedan",
"id": "1.1.10"
},
{
"name": "Station Wagon",
"id": "1.1.11"
},
{
"name": "SUV",
"id": "1.1.12"
},
{
"name": "Van",
"id": "1.1.13"
}
]
},
{
"name": "Auto Buying and Selling",
"id": "1.2"
},
{
"name": "Auto Insurance",
"id": "1.3"
},
{
"name": "Auto Parts",
"id": "1.4"
},
{
"name": "Auto Recalls",
"id": "1.5"
},
{
"name": "Auto Repair",
"id": "1.6"
},
{
"name": "Auto Safety",
"id": "1.7"
},
{
"name": "Auto Shows",
"id": "1.8"
},
{
"name": "Auto Technology",
"id": "1.9",
"categories": [
{
"name": "Auto Infotainment Technologies",
"id": "1.9.1"
},
{
"name": "Auto Navigation Systems",
"id": "1.9.2"
},
{
"name": "Auto Safety Technologies",
"id": "1.9.3"
}
]
},
{
"name": "Auto Type",
"id": "1.10",
"categories": [
{
"name": "Budget Cars",
"id": "1.10.1"
},
{
"name": "Certified Pre-Owned Cars",
"id": "1.10.2"
},
{
"name": "Classic Cars",
"id": "1.10.3"
},
{
"name": "Concept Cars",
"id": "1.10.4"
},
{
"name": "Driverless Cars",
"id": "1.10.5"
},
{
"name": "Green Vehicles",
"id": "1.10.6"
}
]
} ] }
i think what you want is Recursive Descent: ..
cat car.json | jq -r '.. | [.name?, .id?] | select(length>0) | #tsv'
to produce something like in your example,
cat car.json | jq -r '.. | {name:.name?, id:.id?}'

Merge array of hash with same key

I have an array of hash as shown here. I want to merge the values of some fields with custom seprators. Here, i show only two hashes in the array, it is possible to have more. But, they are always in same sequence as shown here.
{
"details": [
{
"place": "abc",
"group": 3,
"year": 2006,
"id": 1304,
"street": "xyz 14",
"lf_number": "0118",
"code": 4433,
"name": "abc coorperation",
"group2": 3817,
"group1": 32,
"postal_code": "22926",
"status": 2
},
{
"place": "cbc",
"group": 2,
"year": 2007,
"id": 4983,
"street": "mnc 14",
"lf_number": "0145",
"code": 4433,
"name": "abc coorperation",
"group2": 3817,
"group1": 32,
"postalcode": "22926",
"status": 2
}
],
"#timestamp": "2017-09-04",
"parent": {
"child": [
{
"w_2": 0.5,
"w_1": 0.1,
"id": 14226,
"name": "air"
},
{
"w_2": null,
"w_1": 91,
"id": 25002,
"name": "Water"
}]
},
"p_name": "anacin",
"#version": "1",
"id": 28841
}
I want to edit the details. I want to construct new fields.
Field 1) coorperations: (details.name | details.postal_code details.street ; details.name | details.postal_code details.street)
Output:
Coorperations: (abc coorperation |22926 xyz 14; abc coorperation | 22926 mnc 14)
Field 2) access_code: (details.status-details.id-details.group1-details.group2-details.group(always two digit)/details.year(only last two digits); details.status-details.id-details.group1-details.group2-details.group(always two digit)/details.year(only last two digits))
Output: access_code (2-32-3817-03-06; 2-32-3817-02-07)
How can I achieve this for all the values in details. Here is how final results should look like.
{
"#timestamp": "2017-09-04",
"parent": {
"child": [
{
"w_2": 0.5,
"w_1": 0.1,
"id": 14226,
"name": "air"
},
{
"w_2": null,
"w_1": 91,
"id": 25002,
"name": "Water"
}]
},
"p_name": "anacin",
"#version": "1",
"id": 28841,
"Coorperations" : "abc coorperation |22926 xyz 14; abc coorperation | 22926 mnc 14",
"access_code" : "2-32-3817-03-06; 2-32-3817-02-07"
}
You can try to run this code in rails console with hash is your json:
new_hash = hash.except(:details)
coorperations = ""
access_code = ""
elements = hash[:details]
elements.each do |element|
coorperations = "#{coorperations}#{if coorperations.present? then '; ' else '' end}#{element[:name]} | #{element[:postal_code]} #{element[:street]}"
access_code = "#{access_code}#{if access_code.present? then '; ' else '' end}#{element[:status]}-#{element[:id]}-#{element[:group1]}-#{element[:group2]}-#{element[:group1]}-#{element[:group]}"
end
new_hash.merge!(Coorperations: coorperations)
new_hash.merge!(access_code: access_code)
new_hash

Resources