How get index of array with jq - arrays

I want search this string tb1qpvtnfqqs3cp4ly4375km7n5sga8hkdkujkm854 in that structure
{
"txid": "67bc5194442dc350312a7c0a5fc7ef912c31bf00b23349b4c3afdf177c91fb2f",
"hash": "8392ded0647e4166eda342cee409c7d0e1e3ffab24de41866d2e6a7bd0a245b3",
"version": 2,
"size": 245,
"vsize": 164,
"weight": 653,
"locktime": 1764124,
"vin": [
{
"txid": "69eed058cbd18b3bf133c8341582adcd76a4d837590d3ae8fa0ffee1d597a8c3",
"vout": 0,
"scriptSig": {
"asm": "0014759fc698313da549948940508df6db93a319096e",
"hex": "160014759fc698313da549948940508df6db93a319096e"
},
"txinwitness": [
"3044022014a8eb758063c52bc970d42013e653f5d3fb3c190b55f7cfa72680280cc5138602202a873b5cad4299b2f52d8cccb4dcfa66fa6ec256d533788f54440d4cdad7dd6501",
"02ec8ba22da03ed1870fe4b9f9071067a6a1fda6f582c5c858644e44bd401bfc0a"
],
"sequence": 4294967294
}
],
"vout": [
{
"value": 0.37841708,
"n": 0,
"scriptPubKey": {
"asm": "0 686bc8ce41505642c96f3eb99919fff63f4c0f11",
"hex": "0014686bc8ce41505642c96f3eb99919fff63f4c0f11",
"reqSigs": 1,
"type": "witness_v0_keyhash",
"addresses": [
"tb1qdp4u3njp2pty9jt086uejx0l7cl5crc3x3phwd"
]
}
},
{
"value": 0.00022000,
"n": 1,
"scriptPubKey": {
"asm": "0 0b173480108e035f92b1f52dbf4e90474f7b36dc",
"hex": "00140b173480108e035f92b1f52dbf4e90474f7b36dc",
"reqSigs": 1,
"type": "witness_v0_keyhash",
"addresses": [
"tb1qpvtnfqqs3cp4ly4375km7n5sga8hkdkujkm854"
]
}
}
],
"hex": "02000000000101c3a897d5e1fe0ffae83a0d5937d8a476cdad821534c833f13b8bd1cb58d0ee690000000017160014759fc698313da549948940508df6db93a319096efeffffff022c6b410200000000160014686bc8ce41505642c96f3eb99919fff63f4c0f11f0550000000000001600140b173480108e035f92b1f52dbf4e90474f7b36dc02473044022014a8eb758063c52bc970d42013e653f5d3fb3c190b55f7cfa72680280cc5138602202a873b5cad4299b2f52d8cccb4dcfa66fa6ec256d533788f54440d4cdad7dd65012102ec8ba22da03ed1870fe4b9f9071067a6a1fda6f582c5c858644e44bd401bfc0a1ceb1a00",
"blockhash": "000000009acb8b4f06a97beb23b3d9aeb3df71052dabec94465933b564c27f50",
"confirmations": 2,
"time": 1591687001,
"blocktime": 1591687001
}
I'd like to get the index of vout, in this case 1. is it possible with jq?

It's not clear what exactly you want.
I guess you want the n of the element of vout that contains the given address in its addresses list. That can be achieved with
jq '.vout[]
| select(.scriptPubKey.addresses[] == "tb1qpvtnfqqs3cp4ly4375km7n5sga8hkdkujkm854")
| .n
' file.json
You can also use
select((.scriptPubKey.addresses[]
| contains("tb1qpvtnfqqs3cp4ly4375km7n5sga8hkdkujkm854")))
to search for the address.

The following assumes that you want the index in .vout of the first object which has the given string as a leaf value, and that you have in mind using 0 as the index origin.
A simple and reasonably efficient jq program that finds all such indices is as follows:
.vout
| range(0;length) as $i
| if any(.[$i]|..;
. == "tb1qpvtnfqqs3cp4ly4375km7n5sga8hkdkujkm854")
then $i
else empty
end
With the given input, this in fact yields 1, which is in accordance with the problem description, so we seem to be on right track.
To get the first index, you could wrap the above in first(...), but in that case the result would be the empty stream if there is no occurrence. So perhaps you would prefer to wrap the above in first(...) // null

You could try something like this:
$vout={{ your json }}
$value="tb1qpvtnfqqs3cp4ly4375km7n5sga8hkdkujkm854"
result=$(echo "$vout" | jq -r '.[0] | select($value)')

Related

How do I provide an incrementing counter in place of an existing JSON value using jq

I have an JSON file similar to this:
{
"version": "2.0",
"stage" : {
"objects" : [
{
"foo" : 1100,
"bar" : false,
"id" : "56a983f1-8111-4abc-a1eb-263d41cfb098"
},
{
"foo" : 1100,
"bar" : false,
"id" : "6369df4b-90c4-4695-8a9c-6bb2b8da5976"
}],
"bish" : "#FFFFFF"
},
"more": "abcd"
}
I would like the output to be exactly the same, with the exception of an incrementing integer in place of the "id" : "guid" - something like:
{
"version": "2.0",
"stage" : {
"objects" : [
{
"foo" : 1100,
"bar" : false,
"id" : 1
},
{
"foo" : 1100,
"bar" : false,
"id" : 2
}],
"bish" : "#FFFFFF"
},
"more": "abcd"
}
I'm new to jq. I can set the id's to a fixed integer with .stage.objects[].id |= 1.
{
"version": "2.0",
"stage": {
"objects": [
{
"foo": 1100,
"bar": false,
"id": 1
},
{
"foo": 1100,
"bar": false,
"id": 1
}
],
"bish": "#FFFFFF"
},
"more": "abcd"
}
I can't figure out the syntax to make the assigned number iterate.
I tried various combinations of map, reduce, to_entries, foreach and other strategies mentioned in answers to similar questions but the data in those examples always consisted of something simple.
You can exploit the fact that to_entries on arrays uses the index as "key", then modify your value:
.stage.objects |= (to_entries | map(.value.id = .key + 1 | .value))
or
.stage.objects |= (to_entries | map(.value += {id: (.key + 1)} | .value))
Output:
{
"version": "2.0",
"stage": {
"objects": [
{
"foo": 1100,
"bar": false,
"id": 1
},
{
"foo": 1100,
"bar": false,
"id": 2
}
],
"bish": "#FFFFFF"
},
"more": "abcd"
}
Here's a variant using reduce to iterate over the keys:
.stage.objects |= reduce keys[] as $i (.; .[$i].id = $i + 1)
{
"version": "2.0",
"stage": {
"objects": [
{
"foo": 1100,
"bar": false,
"id": 1
},
{
"foo": 1100,
"bar": false,
"id": 2
}
],
"bish": "#FFFFFF"
},
"more": "abcd"
}
Demo
Update:
Is there a way to make the search and replace go deep? If the items in the objects array had children arrays with id's, could they be replaced as well?
Of course. You could enhance the LHS of the update to also cover all .children arrays recursively using recurse(.[].children | arrays):
(.stage.objects | recurse(.[].children | arrays)) |=
reduce keys[] as $i (.; .[$i].id = $i + 1)
Demo
Note that in this case each .children array is treated independently, thus numbering starts from 1 in each of them. If you want a continuous numbering instead, it has to be done outside and brought down into the iteration. Here's a solution gathering the target paths using path, numbering them using to_entries, and setting them iteratively using setpath:
reduce (
[path(.stage.objects[] | recurse(.children | arrays[]).id)] | to_entries[]
) as $i (.; setpath($i.value; $i.key + 1))
Demo

Updating a json file using jq while keep the exact same input (except changed value)

To keep things simple, I have that file.js:
{
"devices": [
{
"label": "label1",
"percent": 10
},
{
"label": "label2",
"percent": 20
}
]
}
Since I browsed Google and StackOverflow a lot, I actually know how to update, let's say, the label2 from 20% to 50% :
jq '.devices[] | select(.label == "label2").percent = 50' file.json
But here's the ouput:
{
"label": "label1",
"percent": 10
}
{
"label": "label2",
"percent": 50
}
The devices object (?) is not there anymore.
I'd like to find a way to keep the exact same input format.
I guess I should rid of the pipe, but I don't know how.
I've browsed Google and I found the
to_entries / with_entries / from_entries, that seem to keep the whole thing, but I don't know how to merge eveything together.
Thank you.
Make the traversal and filtering all part of the assignment's LHS using parentheses:
jq '(.devices[] | select(.label == "label2").percent) = 50' file.json
Demo
Alternatively, update |= to a map of the whole .devices array:
jq '.devices |= map(select(.label == "label2").percent = 50)' file.json
Demo
Output:
{
"devices": [
{
"label": "label1",
"percent": 10
},
{
"label": "label2",
"percent": 50
}
]
}

Map array to other array values in JQ

How to map array to other array values in jq?
I have two JSON arrays.
[
{
"date": "2021/9/12",
"rate": 7,
"path": "f"
},
{
"date": "2021/9/13",
"rate": 8,
"path": "f"
},
{
"date": "2021/9/14",
"rate": 8,
"path": "f"
},
]
[
"562949953740755",
"562949953740743",
"562949953740744"
]
I want to have a result like this below.
[
{
"date": "2021/9/12",
"rate": 7,
"path": "f",
"inode": "562949953740755"
},
{
"date": "2021/9/13",
"rate": 8,
"path": "f",
"inode": "562949953740743"
},
{
"date": "2021/9/14",
"rate": 8,
"path": "f",
"inode": "562949953740744"
},
]
I tried:
But I have no clue how to achieve this.
jq -s '.[1] as $file | .[0] | (.[].path) |= (range($file|length) as $i | $file[$i]) ' <(cat a.json) <(cat b.json)
Don't reinvent the transpose wheel.
jq -s 'transpose | map(.[0] + {inode: .[1]})' a.json b.json
Online demo
Use the array file (inode) as reference and slurp its content ahead of processing the original file.
jq --slurpfile inode b.json '
reduce range(0, ($inode[0]|length)) as $d (.; .[$d] += {inode: $inode[0][$d]})' a.json
Note that, this works as long as there are equal number of elements in both your JSON arrays.
Another attempt without involving any "slurps" of the input file (probably faster than the earlier one)
jq -n 'input as $inode | input |
reduce range(0, length) as $d (.; .[$d] += {inode: $inode[$d]})' b.json a.json

How to use jq to produce a cartesian product of two arrays present in the input JSON

I'd like to be able to use jq to output the 'product' of 2 arrays in the input JSON... for example, given the following input JSON:
{
"quantities": [
{
"product": "A",
"quantity": 30
},
{
"product": "B",
"quantity": 10
}
],
"portions": [
{
"customer": "C1",
"percentage": .6
},
{
"customer": "C2",
"percentage": .4
}
]
}
I'd like to produce the following output (or similar...):
[
{
"customer": "C1",
"quantities": [
{
"product": "A",
"quantity": 18
},
{
"product": "B",
"quantity": 6
}
]
},
{
"customer": "C2",
"quantities": [
{
"product": "A",
"quantity": 12
},
{
"product": "B",
"quantity": 4
}
]
}
]
So in other words, for each portion, use its value of percentage, and apply it to each product quantity. Given 2 quantities and 2 portions should yield 4 results.. given 3 quantities and 2 portions should yield 6 results, etc...
I've made some attempts using foreach filters, but to no avail...
I think this will do what you want.
[
.quantities as $q
| .portions[]
| .percentage as $p
| {
customer,
quantities: [
$q[] | .quantity = .quantity * $p
]
}
]
Since you indicated you want the Cartesian product, and that you only gave the sample output as being indicative of what you're looking for, it may be worth mentioning that one can obtain the Cartesian product very simply:
.portions[] + .quantities[]
This produces objects such as:
{
"product": "B",
"quantity": 10,
"customer": "C2",
"percentage": 0.4
}
You could then use reduce or (less efficiently, group_by) to obtain the data in whatever form it is you really want.
For example, assuming .customer is always a string, we could transform
the input into the requested format as follows:
def add_by(f;g): reduce .[] as $x ({}; .[$x|f] += [$x|g]);
[.quantities[] + .portions[]]
| map( {customer, quantities: {product, quantity: (.quantity * .percentage)}} )
| add_by(.customer; .quantities)
| to_entries
| map( {customer: .key, quantities: .value })

JSON: use jq to edit specific values in nested arrays

I'm trying to update values within an array inside an array using the utility jq. I've pasted the sample json below.
More specifically: Within the sheets array, and then within the formulas array, I'd like to change each columnName with a value of "MONTH" to "YEAR". I'd like to do the same for within the sheets array, within the columnStyles array, change each incidence of "MONTH" also to "YEAR"
This jq filter gets me the list of columnNames.
.sheets[1] | .formulas[] | .columnName
How can I edit the entire file in place by just updating the values I want? Do I use map with if?
And what if I wanted to edit a portion of a value? For example, in a forumlaString property, just changing the part of the string that contains MONTH but leaving the rest intact?
{
"version": "6.1.1",
"className": "xyz",
"sheets": [
{
"name": "Pass1",
"sheetId": "95e6c2cd-abbe-46c1-8012-bdf37438b9b7",
"keep": true,
"formulas": [
{
"columnName": "SAMPLE_PROVIDER",
"columnId": "0",
"columnIndex": 0,
"formulaString": "\u003dGROUPBY(#Raw!SAMPLE_PROVIDER)"
},
{
"columnName": "MONTH",
"columnId": "1",
"columnIndex": 1,
"formulaString": "\u003dGROUPBY(#Raw!MONTH)"
}
],
"columnStyles": [
{
"columnId": "0",
"name": "SAMPLE_PROVIDER",
"width": 206,
"thousandSeparator": true
},
{
"columnId": "1",
"name": "MONTH",
"width": 100,
"thousandSeparator": true
}
],
"nextColumnId": 2
},
{
"name": "Transform1",
"sheetId": "49071c1c-fa84-4ae3-92c1-b63175a6b26c",
"keep": true,
"formulas": [
{
"columnName": "SAMPLE_PROVIDER",
"columnId": "0",
"columnIndex": 0,
"formulaString": "\u003d#Pass1!SAMPLE_PROVIDER"
},
{
"columnName": "MONTH",
"columnId": "1",
"columnIndex": 1,
"formulaString": "\u003d#Pass1!MONTH"
}
],
"columnStyles": [
{
"columnId": "0",
"name": "SAMPLE_PROVIDER",
"width": 179,
"thousandSeparator": true
},
{
"columnId": "1",
"name": "MONTH",
"width": 100,
"thousandSeparator": true
}
],
"nextColumnId": 3
}
],
"advancedSchedulingInUse": true,
"errorHandlingMode": "IGNORE"
}
To change the columnName field in the desired containers, you can use
jq '(.sheets[] | .formulas[]? | .columnName | select(.=="MONTH")) |= "YEAR"' tmp.json
(The ? avoids an error if there is no key formula.)
To replace MONTH with YEAR in formula strings, replace each formulaString value with a possible modified string returned by sub.
jq '(.sheets[] | .formulas[]? | .formulaString) |= sub("MONTH"; "YEAR")' tmp.json
(sub requires jq 1.5, compiled with the Oniguruma library.)
To combine these into a single jq filter? I'm not sure yet; I have only a tenuous understanding of why either one alone works.
It looks like you're updating more than just fields in the formulas arrays, but a little bit of everything.
If you want to indiscriminately change all occurrences of the string "MONTH" to "YEAR", you could do this:
(.. | strings) |= sub("MONTH"; "YEAR")
This may be a task for walk/1.
(If your jq does not have walk/1, then you can copy its definition from https://github.com/stedolan/jq/blob/master/src/builtin.jq)
For example, if you want to change "MONTH" to "YEAR" whenever "MONTH" appears as the value of a key in an object, then the following would do the job:
jq 'walk(if type == "object"
then with_entries(.value |= (if . == "MONTH" then "YEAR" else . end))
else . end)' input.json
Equivalently:
jq 'walk(if type == "object"
then with_entries(if .value == "MONTH" then .value = "YEAR" else . end)
else . end)' input.json
These can easily be modified in accordance with similar requirements.

Resources