I have the following list of dictionaries:
data = [
{
"connections": None
},
{
"connections": {
"endpoints": [
{
"id": "ee0d38d3-89fb-4e5b-b9bd-83ee554949ab",
"vlan": 200,
},
{
"id": "192ee48f-ad20-48ad-acae-9c8d33e4687b",
"vlan": 100,
},
]
}
},
{
"connections": {
"endpoints": [
{
"id": "4e6b8844-9a91-4098-aa92-ef97ce89cbed",
"vlan": 200,
},
{
"id": "577fcb45-57ab-4903-be60-5b8ac84b8a09",
"vlan": 100,
},
]
}
},
]
I want to search for 'id' "ee0d38d3-89fb-4e5b-b9bd-83ee554949ab" in data and extract all the vlans for any matching 'id'. So in this example, my resulting variable should only contain a list of matches as such:
[100]
I say a list because it is possible that 'id' "ee0d38d3-89fb-4e5b-b9bd-83ee554949ab" will also have another entry in data but with a different vlan. So you could end up with in my result:
[200, 300]
I tried the following but I get a "TypeError: 'NoneType' object is not subscriptable":
vlans = [d["connections"]["endpoints"]["vlan"] for d in data if d["connections"]["endpoints"]["id"] == id]
You do something like below:
res = [endpoint['vlan'] for item in data if item["connections"] for endpoint in item["connections"]["endpoints"] if endpoint['id']== "ee0d38d3-89fb-4e5b-b9bd-83ee554949ab"]
and if you need unique vlan values you can use
list(set(res))
I have this document structure in the collection:
{"_id":"890138075223711744",
"guildID":"854557773990854707",
"name":"test-lab",
"game": {
"usedWords":["akşam","elma","akım"]
}
}
What is the most efficient way to get its fields except the array (it can be really large), and at the same time, see if an item exists in the array ?
I tried this:
let query = {_id: channelID}
const options = { sort: { name: 1 }, projection: { name: 1, "game.usedWords": { $elemMatch: { word}}}}
mongoClient.db(db).collection("channels").findOne(query, options);
but I got the error: "$elemMatch can not be used on nested fields"
If I've understood correctly you can use this query:
Using positional operator $ you can return only the matched word.
db.collection.find({
"game.usedWords": "akşam"
},
{
"name": 1,
"game.usedWords.$": 1
})
Example here
The output is only name and the matched word (also _id which is returned by default)
[
{
"_id": "890138075223711744",
"game": {
"usedWords": [
"akşam"
]
},
"name": "test-lab"
}
]
I have a JSON:
{
"range": "'All traffic",
"majorDimension": "ROWS",
"values": [
[
"Date",
"Sessions",
"Leeds",
"Сontact_query",
"wo_Сontact_query"
],
[
"20200107",
"3461",
"15",
"0",
"15"
],
[
"20200115",
"7824",
"43",
"0",
"43"
]
]
}
Elements in values array with index[0] are names for JSON properties. I want to map these names with values starting with index[0+]. Number of values can be different for each index. Mapping should be dynamical, without hard indexing.
I want to get following JSON:
[
{
"Date": "20200107",
"Sessions": "3461",
"Leeds": "15",
"Contact_query": "0",
"wo_Contact_query": "15"
},
{
"Date": "20200115",
"Sessions": "7824",
"Leeds": "43",
"Contact_query": "0",
"wo_Contact_query": "43"
}
]
I successfully did it with kinda weird JOLT config:
[
{
"operation": "shift",
"spec": {
"values": {
"0": null,
"*": {
"*": "[&1].#(2,[0].[&])"
}
}
}
},
{
"operation": "shift",
"spec": {
"0": null,
"*": "[]"
}
}
]
But i want to replace it with Groovy script.
I reached following solution:
def slurped = new JsonSlurper().parseText(text)
def val = slurped.values[0]
def json = slurped.values.collect{
[val, it].transpose().collectEntries()
}
But it also returns values for [0] element which one i should skip:
{
"Date":"Date",
"Sessions":"Sessions",
"Leeds":"Leeds",
"Contact_query":"Contact_query",
"wo_Contact_query":"wo_Contact_query"
}
How can I improve solution?
You can use pop to remove the first value of a collection:
def values = new groovy.json.JsonSlurper().parseText(text).values;
def val = values.pop()
def json = values.collect { [val, it].transpose().collectEntries() }
Another option using head and tail:
def json = new JsonSlurper().parseText(data)
def head = json.values.head() // first element, the "head"
def tail = json.values.tail() // the rest, excluding the head, i.e. "tail"
def result = tail.collect { [head, it].transpose().collectEntries() }
Suppose this is my API Response:
{
"name": "hello-world",
"listObjects": [
{
"id": 100,
},
{
"id": 200,
}
]
}
And I want this to be validated with this response:
{
"name": "hello-world",
"listObjects": [
{
"id": 100,
},
{
"id": 200,
}
]
}
For this i'm doing: response == myJson. This works perfectly!
But the listObjects can be in any order. The response sometimes can be like:
"name": "hello-world",
"listObjects": [
{
"id": 200,
},
{
"id": 100,
}
]
}
In such cases how do I do exact json matching in just one line?
I do not want to do individual key matching.
It should be done in one single line.
You need at least 2 lines. Read the docs to understand this more:
* def list = [{ id: 200 }, { id: 100 }]
* def response =
"""
{
"name": "hello-world",
"listObjects": [
{
"id": 100,
},
{
"id": 200,
}
]
}
"""
* match response.listObjects contains only list
* match response == { name: 'hello-world', listObjects: '#(^^list)' }
In this case, first you can sort the response from API explicitly using 'id'. There are several libraries which will sort array using one particular key. Using those, you can sort actual API response first in a way how your expected JSON looks and compare them using equals
Would like to convert the below json record into a text using Groovy
import groovy.json.*
def js = """{
"title": {
"titleid": "222",
"titlename": "ABCD",
"titledesc": null
},
"customer": {
"customerDetail": {
"customerid": 878378743,
"customerstatus": "ACTIVE",
"customersystems": {
"customersystem1": "SYS01",
"customersystem2": null
},
"sysid": null
},
"store": {
"storeid": "LOS002",
"storename": "LAStore",
"areacode": "JDHJ8K988"
},
"persons": {
"person1": {
"personid": "123",
"personname": "IIISKDJKJSD"
},
"person2": {
"personid": "456",
"personname": "IUDFIDIKJK"
}
},
"order": {
"orderdetail": {
"orderid": "4291026",
"ordername": "ORD93999"
}
},
"product": {
"orderdate": "20190101",
"currency": "USD",
"amount": 1000.23
}
}
}
"""
def data = new JsonSlurper().parseText(js)
Expected output should as below with proper header names:
customerId,customerstatus,customersystem1,sysid,storeid,storename,person1.personid,person1.personname,orderid,orderdate,currency,amount,titlename
878378743,ACTIVE,SYS01,null,LOS002,LAStore,123,IIISKDJKJSD,4291026,20190101,USD,1000.23
This is just a single json record so how would I convert all my json records using Groovy?
The following code:
import groovy.json.*
def js = """
[
{
"title": {
"titleid": "222",
"titlename": "ABCD",
"titledesc": null
},
"customer": {
"customerDetail": {
"customerid": 878378743,
"customerstatus": "ACTIVE",
"customersystems": {
"customersystem1": "SYS01",
"customersystem2": null
},
"sysid": null
},
"store": {
"storeid": "LOS002",
"storename": "LAStore",
"areacode": "JDHJ8K988"
},
"persons": {
"person1": {
"personid": "123",
"personname": "IIISKDJKJSD"
},
"person2": {
"personid": "456",
"personname": "IUDFIDIKJK"
}
},
"order": {
"orderdetail": {
"orderid": "4291026",
"ordername": "ORD93999"
}
},
"product": {
"orderdate": "20190101",
"currency": "USD",
"amount": 1000.23
}
}
}
]
"""
/*
customerId,customerstatus,customersystem1,sysid,storeid,storename,person1.personid,person1.personname,orderid,orderdate,currency,amount,titlename
878378743,ACTIVE,SYS01,null,LOS002,LAStore,123,IIISKDJKJSD,4291026,20190101,USD,1000.23
*/
def data = new JsonSlurper().parseText(js)
def mappings = [
customerId: { n -> n.customer.customerDetail.customerid },
customerstatus: { n -> n.customer.customerDetail.customerstatus },
customersystem1: { n -> n.customer.customerDetail.customersystems.customersystem1 },
sysid: { n -> n.customer.customerDetail.sysid },
storeid: { n -> n.customer.store.storeid },
storename: { n -> n.customer.store.storename },
'person1.personid': { n -> n.customer.persons.person1.personid },
'person1.personname': { n -> n.customer.persons.person1.personname },
orderid: { n -> n.customer.order.orderdetail.orderid },
orderdate: { n -> n.customer.product.orderdate },
currency: { n -> n.customer.product.currency },
amount: { n -> n.customer.product.amount },
titlename: { n -> n.title.titlename }
]
def headers = mappings.keySet().join(',') //edited thanks to comment
println headers
data.each { item ->
def row = mappings.collect { k, v -> v(item) }.join(',')
println row
}
does what you ask for. Note that I made the json be a list of items instead of the single item since it seemed from your text that that is what you were after.
Running the above code produces:
~> groovy solution.groovy
customerId,customerstatus,customersystem1,sysid,storeid,storename,person1.personid,person1.personname,orderid,orderdate,currency,amount,titlename
878378743,ACTIVE,SYS01,null,LOS002,LAStore,123,IIISKDJKJSD,4291026,20190101,USD,1000.23,ABCD
~>
note that if this is going into some critical system and is not just a one-off ad-hoc piece of code, you should probably do things like check the return value of v(item) and log some error of otherwise handle when there is no value for a certain path in the json etc.
Should also be noted that the above code relies on the fact that a map literal in groovy (i.e. def mappings = [:]) creates an instance of java's LinkedHashMap which has predictable iteration order for things like keySet() and collect { }.
<< edit >>
For a single item json blob, you would change the code as follows:
def js = """
{
...
}
"""
def item = new JsonSlurper().parseText(js)
def mappings = ...
def headers = mappings.keySet().join(',') //edited thanks to comment
println headers
def row = mappings.collect { k, v -> v(item) }.join(',')
println row
where ... denotes that the block is unchanged from the example above.