Convert list of ordered dict to nested lists - python

I've list of ordered dict that includes some duplicate Ids in data.. something like this
[OrderedDict([('caseId', 20), ('userId', 1), ('emailStatus', 21)]),
OrderedDict([('caseId', 20), ('userId', 1), ('emailStatus', 20)]),
OrderedDict([('caseId', 18), ('userId', 4), ('emailStatus', 21)]),
OrderedDict([('caseId', 19), ('userId', 3), ('emailStatus', 21)]),
OrderedDict([('caseId', 18), ('userId', 1), ('emailStatus', 20)]),
OrderedDict([('caseId', 20), ('userId', 3), ('emailStatus', 21)]),
OrderedDict([('caseId', 18), ('userId', 4), ('emailStatus', 20)]),
OrderedDict([('caseId', 19), ('userId', 1), ('emailStatus', 20)])]
I want to get a list of nested lists, something like this;
[{
"caseId": "20",
"users": [
{
"userId": "1",
"emailStatus": [
{
"emailStatus" : "20"
},
{
"emailStatus" : "21"
}
]
},
{
"userId": "3",
"emailStatus": [
{
"emailStatus" : "21"
}
]
}
]
},
{
"caseId": "19",
"users": [
{
"userId": "1",
"emailStatus": [
{
"emailStatus" : "20"
}
]
},
{
"userId": "3",
"emailStatus": [
{
"emailStatus" : "21"
}
]
}
]
},
{
"caseId": "18",
"users": [
{
"userId": "1",
"emailStatus": [
{
"emailStatus" : "20"
}
]
},
{
"userId": "4",
"emailStatus": [
{
"emailStatus" : "20"
},
{
"emailStatus" : "21"
}
]
}
]
}
]
presenting a nested list like this;
I tried to achieve this by iterating both lists but couldn't get any idea how to keep record of previous and next records and same data.. that's so confusing.. if anyone can give me a start that how I can iterate my list, it would be very kind of you.
Many regards..
Updated Question
More detailed question here

First, you can use a loop and dict.setdefault to group the data in a nested dict:
temp = {}
for d in lst:
temp.setdefault(d["caseId"], {}).setdefault(d["userId"], []).append(d["emailStatus"])
print(temp)
# {18: {1: [20], 4: [21, 20]}, 19: {1: [20], 3: [21]}, 20: {1: [21, 20], 3: [21]}}
Or using a collections.defaultdict:
temp = defaultdict(lambda: defaultdict(list))
for d in lst:
temp[d["caseId"]][d["userId"]].append(d["emailStatus"])
Then, use a nested mixed dict and list comprehension to aggregate your final result:
res = [{"caseId": case, "users": [{"userId": user, "emailStatus": [{"emailStatus": s} for s in status]}
for user, status in users.items()]}
for case, users in temp.items()]
print(res)
# [{'caseId': 18, 'users': [{'userId': 1, 'emailStatus': [{'emailStatus': 20}]}, {'userId': 4, 'emailStatus': [{'emailStatus': 21}, {'emailStatus': 20}]}]},
# {'caseId': 19, 'users': [{'userId': 1, 'emailStatus': [{'emailStatus': 20}]}, {'userId': 3, 'emailStatus': [{'emailStatus': 21}]}]},
# {'caseId': 20, 'users': [{'userId': 1, 'emailStatus': [{'emailStatus': 21}, {'emailStatus': 20}]}, {'userId': 3, 'emailStatus': [{'emailStatus': 21}]}]}]

Related

represent parent and child relation as dictionary

I have a list of dictionary. I want to convert this list into dictionary using parent and child relation. I have try many time. But its difficult for me.
Thanks in advance for solving the problem.
Input =
data = [
{
"_id": 1,
"label": "Property",
"index": 1
},
{
"_id": 2,
"label": "Find Property",
"index": 1,
"parent_id": 1
},
{
"_id": 3,
"label": "Add Property",
"index": 2,
"parent_id": 1
},
{
"_id": 4,
"label": "Offer",
"index": 2
},
{
"_id": 5,
"label": "My Offer",
"index": 1,
"parent_id": 4
},
{
"_id": 6,
"label": "Accept",
"index": 1,
"parent_id": 5
}
]
I have a list of dictionary. I want to convert this list into dictionary using parent and child relation. I have try many time. But its difficult for me.
Thanks in advance for solving the problem.
Expected Output:
[
{
"_id": 1,
"label": "Property",
"index": 1,
"children" : [
{
"_id": 2,
"label": "Find Property",
"index": 1
},
{
"_id": 3,
"label": "Add Property",
"index": 2
}
]
},
{
"_id": 4,
"label": "Offer",
"index": 2,
"children" : [
{
"_id": 5,
"label": "My Offer",
"index": 1,
"children" : [
{
"_id": 6,
"label": "Accept",
"index": 1
}
]
}
]
},
]
I would do it like this. Keep in mind that this solution also affects the original data list.
parents = list()
# First, create a new dict where the key is property id and the value
# is the property itself.
indexed = {d["_id"]:d for d in data}
for id_, item in indexed.items():
# If a property doesn't have "parent_id" key it means that
# this is the root property, appending it to the result list.
if "parent_id" not in item:
parents.append(item)
continue
# Saving parent id for convenience.
p_id = item["parent_id"]
# Adding a children list if a parent doesn't have it yet.
if "children" not in indexed[p_id]:
indexed[p_id]["children"] = list()
indexed[p_id]["children"].append(item)
And the result is:
import pprint
pprint.pprint(parents)
[{'_id': 1,
'children': [{'_id': 2, 'index': 1, 'label': 'Find Property', 'parent_id': 1},
{'_id': 3, 'index': 2, 'label': 'Add Property', 'parent_id': 1}],
'index': 1,
'label': 'Property'},
{'_id': 4,
'children': [{'_id': 5,
'children': [{'_id': 6,
'index': 1,
'label': 'Accept',
'parent_id': 5}],
'index': 1,
'label': 'My Offer',
'parent_id': 4}],
'index': 2,
'label': 'Offer'}]

Create one 'list' by userID

I want to create a list per user so i got this jsonfile:
data = [
{
"id": "1",
"price": 1,
},
{
"id": "1",
"price": 10,
},
{
"id": "2",
"price": 3,
},
{
"id": "1",
"price": 10,
},
{
"id": "2",
"price":8,
},
]
I'm on python and I want to have a result like
for the user with 'id':1 [1,10,10]
and for the user with "id": "2": [3,8]
so two lists corresponding to the prices according to the ids
is it possible to do that in python ?
note, in fact user id are UUID type and randomly generated.
edit: quantity was a mistake all data are price and id, sorry
collections.defaultdict to the rescue.
Assuming you really do have mixed quantitys and prices and you don't care about mixing them into the same list,
from collections import defaultdict
data = [
{
"id": "1",
"price": 1,
},
{
"id": "1",
"price": 10,
},
{
"id": "2",
"quantity": 3,
},
{
"id": "1",
"price": 10,
},
{
"id": "2",
"price": 8,
},
]
by_id = defaultdict(list)
for item in data:
item = item.copy() # we need to mutate the item
id = item.pop("id")
# whatever is the other value in the dict, grab that:
other_value = item.popitem()[1]
by_id[id].append(other_value)
print(dict(by_id))
The output is
{'1': [1, 10, 10], '2': [3, 8]}
If you actually only do have prices, the loop is simpler:
by_id = defaultdict(list)
for item in data:
by_id[item["id"]].append(item.get("price"))
or
by_id = defaultdict(list)
for item in data:
by_id[item["id"]].append(item["price"])
to fail fast when the price is missing.
first :
you structur data : {[]}, is not supported in python.
assume your data is :
my_json = [
{
"id": "1",
"price": 1,
},
{
"id": "1",
"price": 10,
},
{
"id": "2",
"quantity": 3,
},
{
"id": "1",
"price": 10,
},
{
"id": "2",
"price":8,
},
]
then you can achive with this:
results = {}
for data in my_json:
if data.get('id') not in results:
results[data.get('id')] = [data.get('price') or data.get('quantity')]
else:
results[data.get('id')].append(data.get('price') or data.get('quantity'))
print(results)
output:
{'1': [1, 10, 10], '2': [3, 8]}
Maybe like this:
data = [
{
"id": "1",
"price": 1,
},
{
"id": "1",
"price": 10,
},
{
"id": "2",
"quantity": 3,
},
{
"id": "1",
"price": 10,
},
{
"id": "2",
"price": 8,
}
]
result = {}
for item in data:
try:
result[item['id']].append(item.get('price'))
except KeyError:
result[item['id']] = [item.get('price')]
print(result)
Where None is put in place of the missing price for that entry, quantity key ignored.
Result:
{'1': [1, 10, 10], '2': [None, 8]}
A simple loop that enumerates your list (it's not JSON) in conjunction with setdefault() is all you need:
data = [
{
"id": "1",
"price": 1,
},
{
"id": "1",
"price": 10,
},
{
"id": "2",
"price": 3,
},
{
"id": "1",
"price": 10,
},
{
"id": "2",
"price": 8,
}
]
dict_ = {}
for d in data:
dict_.setdefault(d['id'], []).append(d['price'])
print(dict_)
Output:
{'1': [1, 10, 10], '2': [3, 8]}
Note:
This will fail (KeyError) if either 'id' or 'price' is missing from the dictionaries in the list

ordering a dictionary by count of items across a number of key value lists

hopefully he the title is not too confusing, I have a dictionary (sample below) whereby im trying to sort the dictionary by the number of list (dictionary items) across a number of key values beneath a parent. Hopefully the example makes more sense then my description?
{
"data": {
"London": {
"SHOP 1": [
{
"kittens": 10,
"type": "fluffy"
},
{
"puppies": 11,
"type": "squidgy"
}
],
"SHOP 2": [
{
"kittens": 15,
"type": "fluffy"
},
{
"puppies": 3,
"type": "squidgy"
},
{
"fishes": 132,
"type": "floaty"
}
]
},
"Manchester": {
"SHOP 1": [
{
"kittens": 10,
"type": "fluffy"
},
{
"puppies": 11,
"type": "squidgy"
}
],
"SHOP 2": [
{
"kittens": 15,
"type": "fluffy"
},
{
"puppies": 3,
"type": "squidgy"
},
{
"fishes": 132,
"type": "floaty"
}
],
"SHOP 3": [
{
"kittens": 15,
"type": "fluffy"
},
{
"puppies": 3,
"type": "squidgy"
},
]
},
"Edinburgh": {
"SHOP 1": [
{
"kittens": 10,
"type": "fluffy"
},
{
"puppies": 11,
"type": "squidgy"
}
],
"SHOP 2": [
{
"kittens": 15,
"type": "fluffy"
},
],
"SHOP 3": [
{
"puppies": 3,
"type": "squidgy"
},
]
}
}
}
Summary
# London 2 shops, 5 item dictionaries total
# Machester 3 shops, 7 item dictionaries total
# Edinburgh 3 shops, 4 item dictionaries total
Desired sorting would be by total items across the shops, so ordered Manchester, London, Edinburgh
id usually use somethign like the below to sort, but im not sure how to do this oen with it being counting the number of items across a number of keys?
{k: v for k, v in sorted(x.items(), key=lambda item: item[1])}
You need to reverse sort based on the total number of items for each location, which you can generate as:
sum(len(i) for i in s.values())
where s is the shop dictionary for each location.
Putting this into a sorted expression:
dict(sorted(d['data'].items(), key=lambda t:sum(len(i) for i in t[1].values()), reverse=True))
gives:
{
'Manchester': {
'SHOP 1': [{'kittens': 10, 'type': 'fluffy'}, {'puppies': 11, 'type': 'squidgy'}],
'SHOP 2': [{'kittens': 15, 'type': 'fluffy'}, {'puppies': 3, 'type': 'squidgy'}, {'fishes': 132, 'type': 'floaty'}],
'SHOP 3': [{'kittens': 15, 'type': 'fluffy'}, {'puppies': 3, 'type': 'squidgy'}]
},
'London': {
'SHOP 1': [{'kittens': 10, 'type': 'fluffy'}, {'puppies': 11, 'type': 'squidgy'}],
'SHOP 2': [{'kittens': 15, 'type': 'fluffy'}, {'puppies': 3, 'type': 'squidgy'}, {'fishes': 132, 'type': 'floaty'}]
},
'Edinburgh': {
'SHOP 1': [{'kittens': 10, 'type': 'fluffy'}, {'puppies': 11, 'type': 'squidgy'}],
'SHOP 2': [{'kittens': 15, 'type': 'fluffy'}], 'SHOP 3': [{'puppies': 3, 'type': 'squidgy'}]
}
}
No need to make things complex:
adict = adict['data']
result = []
for capital, value in adict.items():
shop_count = len(value)
items = sum([len(obj) for obj in value.values()])
result.append((capital, shop_count, items))
for capital, shop_count, items in sorted(result, key=lambda x: x[2], reverse=True):
print(f'{capital} {shop_count} shops, {items} item dictionaries total')
Output:
Manchester 3 shops, 7 item dictionaries total
London 2 shops, 5 item dictionaries total
Edinburgh 3 shops, 4 item dictionaries total

Python Dictionary transpose rows as column

I have a CSV file that will be imported and converted into a dictionary.
with open(r"DictionaryQuestion.csv", encoding='utf-8-sig') as csvfile:
csvReader = csv.DictReader(csvfile)
for row in map(dict, csvReader):
print(row)
Example Input
I want to be able to transpose the data so that the Discount & NonDiscount rows will be added as columns with their associated amount as well as getting rid of duplicates. Essentially, I want a new dictionary so that I can zip through it.
This is the desired output.
Desired Output as Dictionary
You can use itertools.groupby() to group records by productId and then update your data.
Below I've converted a list which has records same as yours and created new list with data as expected.
data = [
{
"ProductId": "1", "Brand": "Brand1", "rateamount": 1, "rate_type": "Discount"
},
{
"ProductId": "1", "Brand": "Brand1", "rateamount": 2, "rate_type": "NonDiscount"
},
{
"ProductId": "2", "Brand": "Brand2", "rateamount": 3, "rate_type": "Discount"
},
{
"ProductId": "2", "Brand": "Brand2", "rateamount": 4, "rate_type": "NonDiscount"
},
{
"ProductId": "3", "Brand": "Brand3", "rateamount": 5, "rate_type": "Discount"
},
{
"ProductId": "3", "Brand": "Brand3", "rateamount": 6, "rate_type": "NonDiscount"
},
{
"ProductId": "4", "Brand": "Brand4", "rateamount": 7, "rate_type": "Discount"
},
{
"ProductId": "4", "Brand": "Brand4", "rateamount": 2, "rate_type": "NonDiscount"
},
]
Solution
Assuming you data is ordered by productId, otherwise you'll need to order it before grouping.
import itertools
groups = itertools.groupby(data, lambda e: {"ProductId": e["ProductId"], "Brand": e["Brand"]})
output = []
for group, items in groups:
el = dict(group)
for item in items:
if item["rate_type"] == "Discount":
el["Discount"] = item["rateamount"]
else:
el["NonDiscount"] = item["rateamount"]
output.append(el)
print(output)
Above for loop can be converted to a map
import itertools
groups = itertools.groupby(data, lambda e: {"ProductId": e["ProductId"], "Brand": e["Brand"]})
output = map(
lambda group: dict(
**group[0],
**{
item["rate_type"]: item["rateamount"] for item in group[1]
}),
groups
)
print(list(output))
Both prints
[
{'ProductId': '1', 'Brand': 'Brand1', 'Discount': 1, 'NonDiscount': 2},
{'ProductId': '2', 'Brand': 'Brand2', 'Discount': 3, 'NonDiscount': 4},
{'ProductId': '3', 'Brand': 'Brand3', 'Discount': 5, 'NonDiscount': 6},
{'ProductId': '4', 'Brand': 'Brand4', 'Discount': 7, 'NonDiscount': 2}
]

Using itertools library to get combinations of objects?

I want to generate combinations of an object of lists in Python and was looking into itertools.product or similar functions to calculate combinations. Since itertools.product generates combinations from multiple arrays
I have an object that looks like this:
{
"Cities": [
{
"Id": 1,
"Value": "New York"
},
{
"Id": 2,
"Value": "Boston"
}
],
"People": [
{
"Id": 1,
"Value": "Michael"
},
{
"Id": 2,
"Value": "Ryan"
},
{
"Id": 3,
"Value": "Jim"
},
{
"Id": 4,
"Value": "Phyllis"
}
]
}
And I want to generate a list that shows all combinations of people living in each city. So in the case above a list of 8 values.
My code below looks like:
import json
import itertools
def main():
combinations = []
with open('people.json') as f:
data = json.load(f)
combinations = list(itertools.product(*data))
print(combinations)
if __name__ == "__main__":
main()
When running I get a completely different result:
How can I modify my code to get the result I want?
Note: It doesn't have to use itertools, I just thought itertools is used for calculations like these
To perform the job you have described, using the data you have shown, this script will do it:
import json
import itertools
def main():
combinations = []
with open('people.json') as f:
data = json.load(f)
combinations = list(itertools.product(data['Cities'], data['People']))
print(combinations)
if __name__ == "__main__":
main()
The only difference is that I'm specifying which data to use from the data structure.
The output (I have formatted it for readability):
[
({"Id": 1, "Value": "New York"},
{"Id": 1, "Value": "Michael"}),
({"Id": 1, "Value": "New York"},
{"Id": 2, "Value": "Ryan"}),
({"Id": 1, "Value": "New York"},
{"Id": 3, "Value": "Jim"}),
({"Id": 1, "Value": "New York"},
{"Id": 4, "Value": "Phyllis"}),
({"Id": 2, "Value": "Boston"},
{"Id": 1, "Value": "Michael"}),
({"Id": 2, "Value": "Boston"},
{"Id": 2, "Value": "Ryan"}),
({"Id": 2, "Value": "Boston"},
{"Id": 3, "Value": "Jim"}),
({"Id": 2, "Value": "Boston"},
{"Id": 4, "Value": "Phyllis"})
]
If you instead wanted to perform a product between whatever keys are in the data set, you would want to do itertools.product(data.values()), but the code I have shown is clearer.
Why you get the output that you do:
When you do list(itertools.product(*data)), the same thing is passed to product that you see when you do:
for x in data:
print(x)
ie., you did
itertools.product(['Cities', 'People'])
which is why you got the product of the characters in those two strings (yay duck typing!)
[
('C', 'P'),
('C', 'e'),
('C', 'o'),
('C', 'p'),
('C', 'l'),
('C', 'e'),
('i', 'P'),
('i', 'e'),
('i', 'o'),
('i', 'p'),
('i', 'l'),
('i', 'e'),
...
]
How to get the output you want:
You're using product() right, but giving it the wrong data.
cities = [c['Value'] for c in data['Cities']] # Extract all cities Value from list-of-dicts
people = [c['Value'] for c in data['People']] # Extract all people Value from list-of-dicts
print(list(itertools.product(cities, people))) # Product
This gives the output:
[
('New York', 'Michael'),
('New York', 'Ryan'),
('New York', 'Jim'),
('New York', 'Phyllis'),
('Boston', 'Michael'),
('Boston', 'Ryan'),
('Boston', 'Jim'),
('Boston', 'Phyllis')
]
If you want the dict objects instead of the Value keys, you can simply pass those objects to product():
print(list(itertools.product(data['Cities'], data['People']))) # Product
which gives
[
({'Id': 1, 'Value': 'New York'}, {'Id': 1, 'Value': 'Michael'}),
({'Id': 1, 'Value': 'New York'}, {'Id': 2, 'Value': 'Ryan'}),
({'Id': 1, 'Value': 'New York'}, {'Id': 3, 'Value': 'Jim'}),
({'Id': 1, 'Value': 'New York'}, {'Id': 4, 'Value': 'Phyllis'}),
({'Id': 2, 'Value': 'Boston'}, {'Id': 1, 'Value': 'Michael'}),
({'Id': 2, 'Value': 'Boston'}, {'Id': 2, 'Value': 'Ryan'}),
({'Id': 2, 'Value': 'Boston'}, {'Id': 3, 'Value': 'Jim'}),
({'Id': 2, 'Value': 'Boston'}, {'Id': 4, 'Value': 'Phyllis'})
]
as expected.

Categories