Changing the values of a map nested in another map - groovy

Using HQL queries I've been able to generate the following map, where the keys represent the month number constant defined in java.util.Calendar, and every value is a map:
[
0:[ client_a:[order1, order2, order3]],
1:[ client_b:[order4], client_c:[order5, order6], client_d:[order7]],
2:[ client_e:[order8, order9], client_f:[order10]]
]
order1, order2, ... are instances of a domain class called Order:
class Order {
String description
Date d
int quantity
}
Now I've got that structure containing orders that belong to some specific year, but I don't really care about the Order object itself. I just want the sum of the quantities of all the orders of each month. So the structure should look something like this:
[
0:[ client_a:[321]],
1:[ client_b:[9808], client_c:[516], client_d:[20]],
2:[ client_e:[22], client_f:[10065]]
]
I don't mind if the values are lists of one element or not lists at all. If this is possible, it would be fine anyway:
[
0:[ client_a:321 ],
1:[ client_b:9808, client_c:516, client_d:20 ],
2:[ client_e:22, client_f:10065 ]
]
I know I have to apply something like .sum{it.quantity} to every list of orders to get the result I want, but I don't know how to iterate over them as they are nested within another map.
Thank you.

Here You go:
class Order {
String description
Date d
int quantity
}
def orders = [
0:[ client_a:[new Order(quantity:1), new Order(quantity:2), new Order(quantity:3)]],
1:[ client_b:[new Order(quantity:4)], client_c:[new Order(quantity:5), new Order(quantity:6)], client_d:[new Order(quantity:7)]],
2:[ client_e:[new Order(quantity:8), new Order(quantity:9)], client_f:[new Order(quantity:10)]]
]
def count = orders.collectEntries { k, v ->
def nv = v.collectEntries { nk, nv ->
[(nk): nv*.quantity.sum()]
}
[(k):(nv)]
}
assert count == [0:[client_a:6], 1:[client_b:4, client_c:11, client_d:7],2:[client_e:17, client_f:10]]

def map = [
0:[ client_a:[[q: 23], [q: 28], [q: 27]]],
1:[ client_b:[[q: 50]], client_c:[[q: 100], [q: 58]], client_d:[[q: 90]]],
2:[ client_e:[[q: 48], [q: 60]], client_f:[[q: 72]]]
]
map.collectEntries { k, v ->
[ k, v.collectEntries { key, val ->
[ key, val*.q.sum() ]
} ]
}
you can also use val.sum { it.q } instead of val*.q.sum()

Related

Find values from the Nested Dict

Something I'm doing incorrect, but I got data with list of nested dict, I need to pull the value and store in the output dict.
Data:
[{
"encoding":"json",
"result": {
"aclList":[
{
"chipName":"",
"countersEnabled":true,
"countersIncomplete":false,
"dynamic":false,
"name":"FW-RULE-IN",
"readonly":false,
"sequence":[
{
"convertSymbols":true,
"sequenceNumber":1,
"text":"TEST: RULE 1"
},
{
"convertSymbols":true,
"sequenceNumber":2,
"text":"TEST: RULE 2"
},
{
"convertSymbols":true,
"sequenceNumber":3,
"text":"TEST: RULE 3"
},]}]}}]
Like to put sequenceNumber, text values in to output result in Dict.
here is the code I got, but not working, how to fix it?
def parse_data(data):
rules = {}
acllist = []
for k, v in data[0].items():
for h, j in k['result'].items():
for i in h['aclList'][0]:
for l in i[0]['sequence']:
rules['NUM'] = int(j['sequenceNumber'])
rules['TEXT_RULES'] = j['text']
acllist.append(rules.copy())
print (j['sequenceNumber'], j['text'])
return acllist
I'm assuming there can be multiple elements to these lists. If there aren't you could simplify to.
for dct in data[0]['result']['aclList'][0]['sequence']:
rules['NUM'] = int(dct['sequenceNumber'])
rules['TEXT_RULES'] = dct['text']
acllist.append(rules.copy())
print (inner['sequenceNumber'], dct['text'])
However if you want to iterate through every list and gather the elements for each version, it would look more like this:
def parse_data(data):
acllist = []
# for each dict in the initial list
for dct in data:
# for each list in the ['aclList'] key of the ['result'] key
for lst in dct['result']['aclList']:
# for each inner dict in the ['sequence'] key
for inner in lst['sequence']:
rules = {}
rules['NUM'] = int(inner['sequenceNumber'])
rules['TEXT_RULES'] = inner['text']
acllist.append(rules.copy())
print (inner['sequenceNumber'], inner['text'])
return acllist
data =[{
"encoding":"json",
"result": {
"aclList":[
{
"chipName":"",
"countersEnabled":True,
"countersIncomplete":False,
"dynamic":False,
"name":"FW-RULE-IN",
"readonly":False,
"sequence":[
{
"convertSymbols":True,
"sequenceNumber":1,
"text":"TEST: RULE 1"
},
{
"convertSymbols":True,
"sequenceNumber":2,
"text":"TEST: RULE 2"
},
{
"convertSymbols":True,
"sequenceNumber":3,
"text":"TEST: RULE 3"
},]}]}}]
print(parse_data(data))
Where you went wrong is that you are iterating through the key/values of you dicts.
for k, v in data[0].items():
k here would contain "encoding" and "result".
And then you are trying to iterate through those keys.
for h, j in k['result'].items():
But what you are essentially doing here is saying:
k = "result"
for h, j in k['result'].items():
Or:
for h, j in "result"['result'].items():
You can see why this would throw an error.

Accessing Elements in a nested list in python

I am a newbie in python and I have data in a nested list in this format. I want to do something if the value of age in the dictionaries are equal.
[
[
{
"name":"AAAAA",
"age":"12",
"class":"box"
}
],
[
{
"name":"DDDD",
"age":"10",
"class":"space"
}
],
[
{"name":"BBBB"},
{"age":"16"},
{"class":"cage"}
],
[
{
"name":"EEEE",
"age":"20",
"class":"sage"
}
],
]
The main idea is to store all the ages in a list, get list of duplicate ages and access the dictionary using that list
age_list = []
for l in dict_list:
temp_dict = l[0]
age_list.append(temp_dict["age"])
# get set of duplicates in age list
dup_ages = set([x for x in l if l.count(x) > 1])
for index, age in enumerate(age_list):
for dup_age in dup_ages: # do something for a given duplicate age
if dup_age == age:
dict_list[index] # do something in the dict

List comprehension to dynamically populate attribute of object

What’s the correct way to dynamically populate the list given to the attribute Value? At the moment its its fixed to 3 (0-2), but would be more useful if it was based on INSTANCE_PARAMS[i]["instances"].
I was thinking along the lines of list comprehension but unsure how to write it into the code.
for i in INSTANCE_PARAMS:
output = template.add_output([
Output(
str(INSTANCE_PARAMS[i]["name"]).capitalize() + "Ips",
Description="IPs for " + str(INSTANCE_PARAMS[i]["name"]),
Value=Join(",", [
GetAtt(ec2.Instance(INSTANCE_PARAMS[i]["name"] + "0"), "PrivateIp"),
GetAtt(ec2.Instance(INSTANCE_PARAMS[i]["name"] + "1"), "PrivateIp"),
GetAtt(ec2.Instance(INSTANCE_PARAMS[i]["name"] + "2"), "PrivateIp"),
],
),
)
],
)
INSTANCE_PARAMS = {
"masters": {
"name": "master",
"instances": 3,
"image_id": "ami-xxxxxxxxxx",
"instance_type": "t1.micro",
"security_group_id": [
"MasterSG",
"ClusterSG"
],
},
}
Achieved it with the following:
template = Template()
for i in INSTANCE_PARAMS:
# declared a new list
tag_value = []
# got the counter
for r in range(INSTANCE_PARAMS[i]["instances"]):
# added each one to the new list
tag_value.append(GetAtt(ec2.Instance(INSTANCE_PARAMS[i]["name"] + str(r)), "PrivateIP"))
output = template.add_output([
Output(
str(INSTANCE_PARAMS[i]["name"]).capitalize() + "Ips",
Description="IPs for " + str(INSTANCE_PARAMS[i]["name"]),
# passed in the list
Value=Join(",", tag_value,
),
)
],
)
print(template.to_yaml())

How do I use this list as a parameter for this function?

I'm new to Python and I'm using it to write a Spotify app with Spotipy. Basically, I have a dictionary of tracks called topTracks. I can access a track and its name/ID and stuff with
topSongs['items'][0]
topSongs['items'][3]['id']
topSongs['items'][5]['name']
So there's a function I'm trying to use:
recommendations(seed_artists=None, seed_genres=None, seed_tracks=None, limit=20, country=None, **kwargs)
With this function I'm trying to use seed_tracks, which requires a list of track IDs. So ideally I want to input topSongs['items'][0]['id'], topSongs['items'][1]['id'], topSongs['items'][2]['id'], etc. How would I do this? I've read about the * operator but I'm not sure how I can use that or if it applies here.
You can try something like shown below.
ids = [item["id"] for item in topSongs["items"]]
Here, I have just formed a simple example.
>>> topSongs = {
... "items": [
... {
... "id": 1,
... "name": "Alejandro"
... },
... {
... "id": 22,
... "name": "Waiting for the rights"
... }
... ]
... }
>>>
>>> seed_tracks = [item["id"] for item in topSongs["items"]]
>>>
>>> seed_tracks
[1, 22]
>>>
Imp note about using * operator »
* operator is used in this case but for that, you will need to form a list/tuple containing the list of data the function takes. Something like
You have to form all the variables like seed_tracks above.
data = [seed_artists, seed_genres, seed_tracks, limit, country]
And finally,
recommendations(*data)
Imp note about using ** operator »
And if you are willing to use ** operator, the data will look like
data = {"seed_artists": seed_artists, "seed_genres": seed_genres, "seed_tracks": seed_tracks, "limit": limit, "country": country}
Finally,
recommendations(**data)

Mapping list to Map not working

I have a map
["name1":["field1":value1, "field2":value2, "field3":value3],
"name2":["field1":value4, "field2":value5, "field3":value6],
"name3":["field1":value7, "field2":value8, "field3":value9]]
and a list
[name1, name3]
I wanted a result as
["name1":["field1":value1, "field2":value2, "field3":value3],
"name3":["field1":value7, "field2":value8, "field3":value9]]
The code used
result = recomendationOffers.inject( [:] ) { m, v ->
if( !m[ v ] ) {
m[ v ] = []
}
m[ v ] << tariffRecMap[ v.toString() ]
m
}
Now the datatype of the name1 changed from Varchar2(35) to number(10).
I expected the same logic to work but it is not working and I am getting values
["name1":[null], "name3":[null]]
also the value such as 1000000959 is displayed as 1.000000959E9, is this making any difference ?
posting the original values
When I was handling with string, it looked as below
["FBUN-WEB-VIRGIN-10-24-08":["FIXEDLN_ALLOWANCE":0.0,
"OFFER_VERSION_ID":1.000013082E9, "OFFER_TYPE_DESC":"Contract",
"OFFER_NAME":"PM_V 10 50+250 CA", "SMS_ALLOWANCE":250.0,
"VM_TARIFF_FLAG":"N", "IPHONE_IND":"N", "OFFER_MRC":10.5,
"ALLOWANCE08":0.0, "DATA_ALLOWANCE":524288.0, "BB_IND":"N",
"CONTRACT_TERM":24.0, "OFFER_CODE":"FBUN-WEB-VIRGIN-10-24-08",
"ONNET_TEXT_ALLOWANCE":0.0, "VOICE_ALLOWANCE":50.0,
"MMS_ALLOWANCE":0.0, "ONNET_ALLOWANCE":0.0],
Now after the database datatype changed to number from varchar it looks as below where the value in DB is 1000010315
[1.000010315E9:["FIXEDLN_ALLOWANCE":0.0,
"OFFER_VERSION_ID":1.000010315E9, "OFFER_TYPE_DESC":"Sup Voice",
"OFFER_NAME":"VIP - 35 c", "SMS_ALLOWANCE":60000.0,
"VM_TARIFF_FLAG":"N", "IPHONE_IND":"N", "OFFER_MRC":35.0,
"ALLOWANCE08":45000.0, "DATA_ALLOWANCE":2.147483648E9, "BB_IND":"N",
"CONTRACT_TERM":24.0, "OFFER_CODE":"FBUN-MVP-WEB-VIRGIN-35-24-20",
"ONNET_TEXT_ALLOWANCE":0.0, "VOICE_ALLOWANCE":45000.0,
"MMS_ALLOWANCE":0.0, "ONNET_ALLOWANCE":0.0]
Now the datatype of the name1 changed from Varchar2(35) to number(10) ... also the value such as 1000000959 is displayed as 1.000000959E9, is this making any difference ?
Yes, all the difference in the world. That means you're converting a Double (most likely) to a String, and as the String "1000000959" is not equal to "1.000000959E9", you don't get a match.
Not sure from the question which bits are doubles and which bits are Strings... Maybe you could expand with an actual example?
Also, your inject method can be replaced with:
def result = tariffRecMap.subMap( recomendationOffers )

Resources