Is there a way to transform these 2 arrays by using jq, into a set of objects, like in the example down below? - jq

Example json data:
{
"data": [
{
"place": "FM346",
"id": [
"7_day_A",
"7_day_B",
"7_day_C",
"7_day_D"
],
"values": [
0,
30,
23,
43
]
},
{
"place": "LH210",
"id": [
"1_day_A",
"1_day_B",
"1_day_C",
"1_day_D"
],
"values": [
4,
45,
100,
9
]
}
]
}
what i need to transform it into:
{
"data": [
{
"place": "FM346",
"7_day_A": {
"value": 0
},
"7_day_B": {
"value": 30
},
"7_day_C": {
"value": 23
},
"7_day_D": {
"value": 43
}
},
{
"place": "LH210",
"1_day_A": {
"value": 4
},
"1_day_B": {
"value": 45
},
"1_day_C": {
"value": 100
},
"1_day_D": {
"value": 9
}
}
]
}
i have tried this:
{
data:[.data |.[]|
{
place: (.place),
(.id[]):
{
value: (.values[])
}
}]
}
(in jqplay: https://jqplay.org/s/f4BBtN9gwmp)
and this:
{
data:[.data |.[]|
{
place: (.place),
test:
[{
(.id[]):
{
value: (.values[])
}
}]
}]
}
(in jqplay: https://jqplay.org/s/pKIvQe1CzgX)
but they arent grouped in the way i wanted and it gives each value to each id, not the corresponding one.
I have been trying for some time now, but im new to jq and have no idea how to transform it this way, thanks in advance for any answers.

You can use transpose here, which can play a key role in converting the arrays to key/value pairs
.data[] |= {place} +
([ .id, .values ] | transpose | map({(.[0]): { value: .[1] } }) | add)
The solution works by converting the array-of-arrays [.id, .values] by transposing them, i.e. converting
[["7_day_A","7_day_B","7_day_C","7_day_D"],[0,30,23,43]]
[["1_day_A","1_day_B","1_day_C","1_day_D"],[4,45,100,9]]
to
[["7_day_A",0],["7_day_B",30],["7_day_C",23],["7_day_D",43]]
[["1_day_A",4],["1_day_B",45],["1_day_C",100],["1_day_D",9]]
With the transformation done, we construct an object with key as the zeroth index element and value as an object comprising of the value of first index element, and combine the results together with add
Demo - jqplay

Related

Using jq to conditionally flatten recursive json

The simplest version of the input document I could come up with is
{
"references": [
{
"version": 5,
"id": "id1",
"objType": "A"
},
{
"version": 4,
"id": "id2",
"objType": "B",
"referencing": []
},
{
"version": 4,
"id": "id3",
"objType": "B",
"referencing": [
{
"version": 2,
"id": "id4",
"objType": "A"
},
{
"version": 3,
"id": "id5",
"objType": "B",
"referencing": []
}
]
}
]
}
Objects of type A have no referencing objects.
Objects of type B can be referenced by either type of object.
There are two outputs I need from this json:
Output #1 is the version info for objects of type A with the id value as a key with the value of version. A objects can be at the top level or at some arbitrary depth in the referencing arrays.
{
"references": {
"id1": {"version": 5},
"id4": {"version": 2}
}
}
The 2nd output is similar: the version info for objects of type B. The can be a chain of type B objects referencing other type B objects.
{
"references": {
"id2": {"version": 4},
"id3": {"version": 4},
"id5": {"version": 3}
}
}
Use recursive decsent operator and from_entries. You don't need to follow the "references" (at least not to produce the expected output in your question)
{
dependencies: [.. | select(.objType=="A")? | { key: .id, value: {version} }] | from_entries
},
{
dependencies: [.. | select(.objType=="B")? | { key: .id, value: {version} }] | from_entries
}
Output:
{
"dependencies": {
"id1": {
"version": 5
},
"id4": {
"version": 2
}
}
}
{
"dependencies": {
"id2": {
"version": 4
},
"id3": {
"version": 4
},
"id5": {
"version": 3
}
}
}
It's also possible to merge (add) objects instead of constructing them from their entries, which makes the code minimally shorter:
{
dependencies: [.. | select(.objType=="A")? | { (.id): {version} }] | add
}
You can use recurse to traverse the document, INDEX to create an object with IDs as keys, map_values to format their values using select to reduce according to your criteria.
jq --arg type A '
.references |= (
INDEX(.[] | recurse(.referencing[]?); .id)
| map_values(select(.objType == $type) | {version})
)
'
{
"references": {
"id1": {
"version": 5
},
"id4": {
"version": 2
}
}
}
Demo
This works for both questions, provide A or B to --arg type.
Note that this is using the error suppression operator ? when recursing down. If you want to restrict the traversal explicitly to .objType == "B", just prepend it in a select expression, i.e. replace recurse(.referencing[]?) with recurse(select(.objType == "B") | .referencing[]). Demo

JQ does not contain for "key": "value" JSON structure

The question is similar to THIS, but the JSON structure is different.
In my usecase JSON has an arrays with key:value data:
{
"data":[
{
"name":"banana",
"tags":[
{
"id":"yellow"
},
{
"id":"long"
}
]
},
{
"name":"apple",
"tags":[
{
"id":"red"
},
{
"id":"round"
}
]
},
{
"name":"orange",
"tags":[
{
"id":"orange"
},
{
"id":"round"
},
{
"id":"colored"
}
]
}
]
}
What required is to filter the only elements that do not have certain keyword - "red" for instance.
When i use jq '.data[] | select(.tags[].id | index( "red" ))' it brings me the correct resut of 'apple' (as it has "id": "red") and 'orange' (as it has "id": "colored").
However, when i add the negation jq '.data[] | select(.tags[].id | index( "red" ) | not)' the results are more than strange, with elements' duplication, totally enigmatic.
How can i use jq to filter the result the way it returns only elements that do not have the exact match among the array values?
You can use all to give a condition all items must meet. Here, all values in .tags[].id must be unequal != to "red":
jq '.data[] | select(all(.tags[].id; . != "red"))'
{
"name": "banana",
"tags": [
{
"id": "yellow"
},
{
"id": "long"
}
]
}
{
"name": "orange",
"tags": [
{
"id": "orange"
},
{
"id": "round"
},
{
"id": "colored"
}
]
}
Demo
Add .name and use the -r option to only get the names:
jq -r '.data[] | select(all(.tags[].id; . != "red")).name'
banana
orange
Demo
index works on an array, your current filter does not pass index to an array, therefore you're getting other results then expected.
.data[] | select([ .tags[].id ] | index("red") | not)
Here we create an array with all the id's [ .tags[].id ] and use that array to check for red: | index("red") | not
The above filter gives the following output:
{
"name": "banana",
"tags": [
{
"id": "yellow"
},
{
"id": "long"
}
]
}
{
"name": "orange",
"tags": [
{
"id": "orange"
},
{
"id": "round"
},
{
"id": "colored"
}
]
}
Demo
If you want to exclude 'colored', use contains():
.data[] | select([ .tags[].id ] | contains(["red"]) | not)
{
"name": "banana",
"tags": [
{
"id": "yellow"
},
{
"id": "long"
}
]
}
Demo

CosmosDB, help flatten an array

I'm trying to flatten and filter my json data that is in a CosmosDB. The data looks like below and I would like to flatten everything in the array Variables. I need to show the id, amount and the array of orderidentifiers( only the orderId field)
my json:
{
"id": "b71687be180da8116208cbb9a40e7e5e630e6cd595f3e09040a155978a2169f3",
"amount": 5354.39,
"orderIdentifiers": [
{
"orderId": "16520328183646646587",
"itemIds": [
"90420839-2769-3acc-a686-3171386190a7"
]
},
{
"orderId": "45288779686596595261",
"itemIds": [
"fb662e41-1c7e-3f36-8cfd-ef2f3c7f0752",
"0b97371c-4eb9-3ec5-8ab9-bb65a9c9efe1"
]
},
{
"orderId": "859986489484974993023",
"itemIds": [
"5c16bb0d-f1a4-3289-bda5-28d0b09a2a56",
"ffb5dc2a-6e43-321d-97cb-3279ddfd1e39",
"781953ad-83e0-30da-a563-e69ed2a752c6",
"1936e885-c41a-3bd1-a3e0-80e7e3089fe4"
]
},
{
"orderId": "80455639909013091834",
"itemIds": [
"147d4a11-6c05-3fe3-8e4c-bcda3d238845",
"6c586585-6355-393d-a6f4-6fa6c665f3b8"
]
}
]
}
when I run this query :
select c.id, c.amount, oi.orderId
from finalcategorysales c
JOIN oi IN c.orderIdentifiers
where c.id='b71687be180da8116208cbb9a40e7e5e630e6cd595f3e09040a155978a2169f3'
the result is showing me only one element in orderIdentifiers array
{ "id": "b71687be180da8116208cbb9a40e7e5e630e6cd595f3e09040a155978a2169f3",
"amount": 5354.39,
"orderId": "16520328183646646587"
},
You can try something like this SQL:
SELECT
c.id, c.amount, Array(SELECT oi.orderId FROM c JOIN oi IN c.orderIdentifiers) AS orderIds
FROM c
WHERE c.id='b71687be180da8116208cbb9a40e7e5e630e6cd595f3e09040a155978a2169f3'
Result:
[
{
"id": "b71687be180da8116208cbb9a40e7e5e630e6cd595f3e09040a155978a2169f3",
"amount": 5354.39,
"orderIds": [
{
"orderId": "16520328183646646587"
},
{
"orderId": "45288779686596595261"
},
{
"orderId": "859986489484974993023"
},
{
"orderId": "80455639909013091834"
}
]
}
]

How can I duplicate multiple times an existing object within a JSON array using jq?

I have the following json file:
{
"actions": [
{
"values": "test",
"features": [
{
"v1": 100,
"v2": {
"dates": [
"2020-04-08 06:58:26",
"2020-04-08 06:58:26"
]
}
}
]
}
]
}
I would like to append n-times the object within the "actions" array to the end of it, creating n+1 total objects.
Expected output if n=2:
{
"actions": [
{
"values": "test",
"features": [
{
"v1": 100,
"v2": {
"dates": [
"2020-04-08 06:58:26",
"2020-04-08 06:58:26"
]
}
}
]
},
{
"values": "test",
"features": [
{
"v1": 100,
"v2": {
"dates": [
"2020-04-08 06:58:26",
"2020-04-08 06:58:26"
]
}
}
]
},
{
"values": "test",
"features": [
{
"v1": 100,
"v2": {
"dates": [
"2020-04-08 06:58:26",
"2020-04-08 06:58:26"
]
}
}
]
}
]
}
I found this answer [How can I duplicate an existing object within a JSON array using jq? however it only works with one element at the end.
You can just use a reduce() function with range() together to create the index to include the object at.
jq --arg n 2 'reduce range(0, ($n|tonumber)) as $d (.; .actions[$d+1] += .actions[0] )' json

Create an object with specified indexes

I am trying to use for loop for every object using jq.
Sample Input generated by Elasticsearch
{
"took": 202,
"timed_out": false,
"aggregations": {
"aggsDateHistogram": {
"buckets": [
{
"key": 1465974236000,
"search": {
"value": 14
}
},
{
"key": 1465975137000,
"search": {
"value": 16
}
}
]
}
}
}
I want to have an object that has a key value and corresponding value of value index from search.
{ "date": .aggregations.aggsDateHistogram.buckets[].key, "value": .aggregations.aggsDateHistogram.buckets[].search.value }
This gives me an object but with cartesian product, but I only want to have values like
key[1] : search[1].value
key[2] : search[2].value
So you want to produce this output?
[
{
"key": 1465974236000,
"value": 14
},
{
"key": 1465975137000,
"value": 16
}
]
The following will do just that:
.aggregations[].buckets
| map({key: .key, value: .search.value})
And from a terminal:
jq '.aggregations[].buckets
| map({key: .key, value: .search.value})' input.json
Here is a slightly simpler solution
[ .aggregations[].buckets[] | {key, value:.search.value} ]

Resources