how to parse json with various arrays - json

I have a json file that looks like:
[
{
"id": "aaa",
"idMembers": [
"David",
"Mary"
],
"actions": [
{
"id": "1",
"date": "2019-08-28"
},
{
"id": "2",
"date": "2019-08-29"
},
{
"id": "3",
"date": "2019-08-30"
}
]
},
{
"id": "bbb",
"idMembers": [
"Mar",
"Alex"
],
"actions": [
{
"id": "1",
"date": "2019-07-28"
},
{
"id": "2",
"date": "2019-07-29"
}
]
}
]
I would like to obtain a result like:
["David", "Mary", "1", "2019-08-28"]
["David", "Mary", "2", "2019-08-29"]
["David", "Mary", "3", "2019-08-30"]
["Mar", "Alex", "1", "2019-07-28"]
["Mar", "Alex", "2", "2019-07-29"]
I tried:
jq -c '.[] | [ .idMembers[], .actions[].id, .actions[].date] '
But results are:
["David", "Mary", "1", "2", "3", "2019-08-28", "2019-08-29", "2019-08-30"]
["Mar", "Alex", "1", "2", "2019-07-28", "2019-07-29"]
I would like do someting like:
jq -c '.[] | .idMembers[], .actions[] | [ .id, .date] '
but it return me
jq: error (at :1268): Cannot index string with string "id"
Is possible to do something similar to this?
jq -c '.[] | .actions[] | [.idMembers[], .id, .date] '

Make an array out of each object under actions and add it to idMembers.
.[] | .idMembers + (.actions[] | map(.))
map(.) can also be written as [.[]]. For clarification, above is the same as:
.[] | .idMembers + (.actions[0] | map(.)),
.idMembers + (.actions[1] | map(.)),
.idMembers + (.actions[2] | map(.)),
...
.idMembers + (.actions[n] | map(.))
where n is the number of elements in actions.

Related

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

How do I use jq to flatten a complex json structure?

I have the following simplified json structure: Notice an array of values, which have children, whose children could have children.
{
"value": [
{
"id": "12",
"text": "Beverages",
"state": "closed",
"attributes": null,
"iconCls": null
},
{
"id": "10",
"text": "Foods",
"state": "closed",
"attributes": null,
"iconCls": null,
"children": [
{
"id": "33",
"text": "Mexican",
"state": "closed",
"attributes": null,
"iconCls": null,
"children": [
{
"id": "6100",
"text": "Taco",
"count": "3",
"attributes": null,
"iconCls": ""
}
]
}
]
}
]
}
How do I flatten a json structure using jq? I would like to print each element just once, but in a flat structure. An example output:
{
"id": "12",
"category": "Beverages"
},
{
"id": "10",
"category": "Foods"
},
{
"id": "33",
"category": "Mexican"
},
{
"id": "6100",
"category": "Tacos"
}
My attempt doesn't seem to work at all:
cat simple.json - | jq '.value[] | {id: .id, category: .text} + {id: .children[]?.id, category: .children[]?.text}'
.. is your friend:
.. | objects | select( .id and .text) | {id, category: .text}
If your actual input is that simple, recursively extracting id and text from each object under value should work.
[ .value | recurse | objects | {id, category: .text} ]
Online demo
I was totally going in the wrong direction
Not really. Going in that direction, you would have something like:
.value[]
| recurse(.children[]?)
| {id, category: .text}

jq slurp and add key / value pairs

$DATA is a long string containing some Email addresses.
echo "$DATA" | grep -Eo "\b[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,6}\b" | sort | uniq | jq --slurp --raw-input 'split("\n")[:-1]'
Output:
[
"email1#mydomain.com",
"email2#mydomain.com",
"email3#mydomain.com",
"email4#mydomain.com"
]
Desired Output:
[
{
"email": "email1#mydomain.com",
"free": "0",
"used": "0"
},
{
"email": "email2#mydomain.com",
"free": "0",
"used": "0"
},
{
"email": "email3#mydomain.com",
"free": "0",
"used": "0"
},
{
"email": "email4#mydomain.com",
"free": "0",
"used": "0"
}
]
I guess it should be something like += {"free": "0"}
You can replace your current jq command by the following :
jq --slurp --raw-input 'split("\n")[:-1] | map({email: ., free: 0, used: 0})'
You can try it here.

Splitting nested arrays as separate entities

I have some JSON data which contains attributes and some array elements. I would like to push a given set of fields into the array elements and then separate the arrays as separate entities.
Source data looks like this
[
{
"phones": [
{
"phone": "555-555-1234",
"type": "home"
},
{
"phone": "555-555-5678",
"type": "mobile"
}
],
"email": [
{
"email": "a#b.com",
"type": "work"
},
{
"email": "x#c.com",
"type": "home"
}
],
"name": "john doe",
"year": "2012",
"city": "cupertino",
"zip": "555004"
},
{
"phones": [
{
"phone": "555-666-1234",
"type": "home"
},
{
"phone": "555-666-5678",
"type": "mobile"
}
],
"email": [
{
"email": "a#b.com",
"type": "work"
},
{
"email": "x#c.com",
"type": "home"
}
],
"name": "jane doe",
"year": "2000",
"city": "los angeles",
"zip": "555004"
}
]
I expect a result like this
{
"person": [
{
"name": "john doe",
"year": "2012",
"city": "cupertino",
"zip": "555004"
},
{
"name": "jane doe",
"year": "2000",
"city": "los angeles",
"zip": "555004"
}
],
"phones": [
{
"name": "john doe",
"year": "2012",
"phone": "555-555-1234",
"type": "home"
},
{
"name": "john doe",
"year": "2012",
"phone": "555-555-5678",
"type": "mobile"
},
{
"name": "jane doe",
"year": "2000",
"phone": "555-666-1234",
"type": "home"
},
{
"name": "jane doe",
"year": "2000",
"phone": "555-666-5678",
"type": "mobile"
}
],
"email": [
{
"name": "john doe",
"year": "2012",
"email": "a#b.com",
"type": "work"
},
{
"name": "john doe",
"year": "2012",
"email": "x#c.com",
"type": "home"
},
{
"name": "jane doe",
"year": "2000",
"email": "a#b.com",
"type": "work"
},
{
"name": "jane doe",
"year": "2000",
"email": "x#c.com",
"type": "home"
}
]
}
I have been able to get the desired result, but I can't make it work in a generic way.
experiment on jqterm
The code below achieves the job, but I would like to pass the array of columns to be injected into the child arrays, the name of the primary result and an array containing the array field names.
["phones", "email"] as $children
| ["name", "year"] as $ids
|{person: map(with_entries(
. as $data | select($children|contains([$data.key])|not)
))}
+ {"phones": split_child($children[0];$ids)}
+ {"email": split_child($children[1];$ids)}
It's a lot more easier to achieve this using multiple reduces, like:
def split_data($parent; $ids; $arr_cols):
($arr_cols | map([.])) as $p
| reduce .[] as $in ({}; .[$parent] += [$in | delpaths($p)]
| (reduce $ids[] as $k ({}; . + {($k): $in[$k]}) as $s
| reduce $arr_cols[] as $k (.; .[$k] += [$in[$k][] + $s])
);
split_data("person"; ["name", "year"]; ["phones", "email"])
Here's a straightforward solution to the generic problem (it uses reduce only once, in a helper function). To understand it, it might be helpful to see it as an abstraction of this concrete solution:
{ person: [.[] | {name, year, city, zip} ]}
+ { phones: [.[] | ({name, year} + .phones[]) ]}
+ { email: [.[] | ({name, year} + .email[]) ]}
Helper function
Let's first define a helper function for constructing an object by selecting a set of keys:
def pick($ary):
. as $in
| reduce $ary[] as $k ({};
. + {($k): $in[$k]});
split_data
Here finally is the function that takes as arguments the $parent, $ids, and columns of interest. The main complication is ensuring that the supplemental keys ("city" and "zip") are dealt with in the proper order.
def split_data($parent; $ids; $arr_cols):
(.[0]|keys_unsorted - $arr_cols - $ids) as $extra
| { ($parent): [.[] | pick($ids + $extra)] }
+ ([$arr_cols[] as $k
| {($k): [.[] | pick($ids) + .[$k][]] }] | add) ;
The invocation:
split_data("person"; ["name", "year"]; ["phones", "email"])
produces the desired result.

Have jq return the index number of an element in an array

I want jq to return the index of an alement in an array based on a search.
Using the below array I call jq to find the value "FooBar" of a key "text":
jq '.arr[] | .[3].text | tostring | select(contains("FooBar") )' < file.json
The result is:
"FooBar"
But what I actually want is the index of the most outer array "arr" that this text:FooBar pair is nested in, which is 0 in this case.
Can this e achieved in jq?
{
"arr": [
[
"create",
"w71",
"rwt.widgets.Label",
{
"parent": "w68",
"style": "1",
"bounds": "2",
"tabIndex": -1,
"background": "ww",
"font": "test",
"text": "FooBar",
"alignment": "right"
}
],
[
"create",
"w72",
"rwt.widgets.Label",
{
"parent": "w68",
"style": "22",
"bounds": "1",
"tabIndex": -1,
"foreground": "null",
"background": "1",
"font": "2",
"text": "55",
"alignment": "right"
}
]
]
}
You can first convert the elements in the array to entries, this way both the key and the value are present in the output:
jq '.arr | to_entries'
Give the result where the key is present in the output:
[
{
"key": 0,
"value": [
"create",
"w71",
"rwt.widgets.Label",
{
"parent": "w68",
"style": "1",
"bounds": "2",
"tabIndex": -1,
"background": "ww",
"font": "test",
"text": "FooBar",
"alignment": "right"
}
]
},
{
"key": 1,
"value": [
"create",
"w72",
"rwt.widgets.Label",
{
"parent": "w68",
"style": "22",
"bounds": "1",
"tabIndex": -1,
"foreground": "null",
"background": "1",
"font": "2",
"text": "55",
"alignment": "right"
}
]
}
]
Performing your filtering and returning the index then becomes fairly trivial:
jq '.arr | to_entries | .[] | select(.value[3].text | contains("FooBar")) | .key' <test.json
Here's a solution that does not depend on the assumption that the object of interest has a fixed position within the array:
.arr
| map( .[] | objects | .text )
| index("FooBar")
More robustly:
.arr
| map( first(.[] | objects) // null | .text )
| index("FooBar")