Better way to reorder list of dictionaries? - python

So I have a small data like this:
data = [
{"Name":"Arab","Code":"Zl"},
{"Name":"Korea","Code":"Bl"},
{"Name":"China","Code":"Bz"}
]
I want to find a graph so that the x-axis is: "Bl", "Bz", "Zl" (alphabetic order)
and the y-axis is: "Korea", "China", "Arab" (corresponding to the codenames).
I thought of:
new_data = {}
for dic in data:
country_data = dic["Name"]
code_data = dic["Code"]
new_data[code_data] = country_data
code_data = []
for codes in new_data.keys():
code_data.append(codes)
code_data.sort()
name_data = []
for code in code_data:
name_data.append(new_data[code])
Is there a better way to do this?
Perhaps by not creating a new dictionary?

So here's the data:
data = [
{"Name":"Arab","Code":"Zl"},
{"Name":"Korea","Code":"Bl"},
{"Name":"China","Code":"Bz"}
]
To create a new sorted list:
new_list = sorted(data, key=lambda k: k['Code'])
If you don't want to get a new list:
data[:] = sorted(data, key=lambda k: k['Code'])
The result is:
[{'Code': 'Bl', 'Name': 'Korea'}, {'Code': 'Bz', 'Name': 'China'}, {'Code': 'Zl', 'Name': 'Arab'}]
I hope I could help you!

Better way to produce same results:
from operator import itemgetter
data = [
{"Name": "Arab", "Code": "Zl"},
{"Name": "Korea", "Code": "Bl"},
{"Name": "China", "Code": "Bz"}
]
sorted_data = ((d["Code"], d["Name"]) for d in sorted(data, key=itemgetter("Code")))
code_data, name_data = (list(item) for item in zip(*sorted_data))
print(code_data) # -> ['Bl', 'Bz', 'Zl']
print(name_data) # -> ['Korea', 'China', 'Arab']

Here's one way using operator.itemgetter and unpacking via zip:
from operator import itemgetter
_, data_sorted = zip(*sorted(enumerate(data), key=lambda x: x[1]['Code']))
codes, names = zip(*map(itemgetter('Code', 'Name'), data_sorted))
print(codes)
# ('Bl', 'Bz', 'Zl')
print(names)
# ('Korea', 'China', 'Arab')

Related

Writing nested loop as list comprehension

I have the following for loop I need to make Pythonic by using a list comprehension:
accounts_list = []
for a in Accounts:
rows = {
'Account': a["FullyQualifiedName"],
'Classification': a["Classification"],
'AccountType': a['AccountType']
}
accounts_list.append(rows)
accounts_df = pd.DataFrame(accounts_list)
Here is how:
accounts_list = [{'Account': a["FullyQualifiedName"],
'Classification': a["Classification"],
'AccountType': a['AccountType']} for a in Accounts]
accounts_df = pd.DataFrame(accounts_list)
Explanation:
This is a basic list comprehension:
[a for a in Accounts]
where the returned value of [a for a in Accounts] is the same as Accounts, if rows is of type list.
Add the dictionary for each iteration, and get
accounts_list = [{'Account': a["FullyQualifiedName"],
'Classification': a["Classification"],
'AccountType': a['AccountType']} for a in Accounts]
You could prepare a key mapping list and use it to build your target list items:
keyMap = [ ['Account','FullyQualifiedName'],
['Classification']*2, ['AccountType']*2 ]
accounts_list = [ {k:a[v] for k,v in keyMap} for a in Accounts ]

Combining objects in one list

I have a list of dicts that looks like this:
{
"Player_Name":"Byeong-Hun An",
"Tournament":[
{
"Name":"Arnold Palmer Invitational presented by Mastercard",
"Points":"32.80",
"Salary":"10300.00"
}
]
},
{
"Player_Name":"Byeong-Hun An",
"Tournament":[
{
"Name":"Different",
"Points":"18.80",
"Salary":"10400.00"
}
]
}
and I want this:
[
{
"Player_Name":"Byeong-Hun An",
"Tournament":[
{
"Name":"Arnold Palmer Invitational presented by Mastercard",
"Points":"32.80",
"Salary":"10300.00"
},
{
"Name":"Different",
"Points":"18.80",
"Salary":"10400.00"
}
]
}
]
I've tried collections, but it doesn't do exactly what I'm wanting. I essentially want to take every single player and combine all the tournament objects into one so each player has one object instead of each event having its own object.
Here's my code
import json
import numpy as np
import pandas as pd
from collections import Counter
# using json open the player objects file and set it equal to data
with open('PGA_Player_Objects.json') as json_file:
data = json.load(json_file)
points = []
players = []
for a in data:
for b in a['Tournament']:
points.append(int(float(b['Points'])))
for x in data:
players.append(x['Player_Name'])
def Average(lst):
unrounded = sum(lst) / len(lst)
return round(unrounded,2)
result = Counter()
for d in data:
for b in d['Tournament']:
result[d['Player_Name']] += int(float(b['Points']))
How can I do that?
if your list is in l:
l = [{'Player_Name': 'Byeong-Hun An', 'Tournament': [{'Name': 'Arnold Palmer Invitational presented by Mastercard', 'Points': '32.80', 'Salary': '10300.00'}]},
{'Player_Name': 'Byeong-Hun An', 'Tournament': [{'Name': 'Different', 'Points': '18.80', 'Salary': '10400.00'}]},]
Try this:
from itertools import groupby
result = []
for k,g in groupby(sorted(l, key=lambda x:x['Player_Name']), lambda x:x['Player_Name']):
result.append({'Player_Name':k, 'Tournament':[i['Tournament'][0] for i in g]})
Then the result will be:
[{'Player_Name': 'Byeong-Hun An',
'Tournament': [
{'Name': 'Arnold Palmer Invitational presented by Mastercard',
'Points': '32.80',
'Salary': '10300.00'},
{'Name': 'Different',
'Points': '18.80',
'Salary': '10400.00'}]}]
This works as well, and it's a more general solution that works for arbitrary key names:
from collections import defaultdict
d = defaultdict(list)
for dic in lst:
for k, v in dic.items():
if isinstance(v, list):
d[k].extend(v)
else:
d[k] = v
answer = [dict(d)]
Here's my take on a solution.
Create a new list of dictionaries
Iterate through the original list of dictionaries.
Store one copy of the beginning data for each player that is the same into the new list of dictionaries
Append additional Tournament data for each player into that one dictionary into a unified Tournament list.
Untested code below as an example, but should work with some tweaks.
listofDicts = [{'Player_Name': 'Byeong-Hun An', 'Tournament': [{'Name': 'Arnold Palmer Invitational presented by Mastercard', 'Points': '32.80', 'Salary': '10300.00'}]},{'Player_Name': 'Byeong-Hun An', 'Tournament': [{'Name': 'Different', 'Points': '18.80', 'Salary': '10400.00'}]}]
newListOfDicts = []
playerName = " "
playerNo = -1
for dicts in listofDicts:
if playerName == dicts['Player_Name']:
newListOfDicts[playerNo]['Tournament'].append(dicts['Tournament'][0])
else:
newListOfDicts.append(dicts)
playerName = dicts['Player_Name']
playerNo += 1

How to conditionally select elements in a list comprehension?

I couldn't find any examples that match my use case. Still working through my way in python lists and dictionaries.
Problem:
all_cars = {'total_count': 3,'cars': [{'name': 'audi','model': 'S7'}, {'name': 'honda', 'model': 'accord'},{'name': 'jeep', 'model': 'wrangler'} ]}
owners = {'users':[{'owner': 'Nick', 'car': 'audi'},{'owner': 'Jim', 'car': 'ford'},{'owner': 'Mike', 'car': 'mercedes'} ]}
def duplicate():
for c in all_cars['cars']:
if c['name'] == [c['users']for c in owners['users']]:
pass
else:
res = print(c['name'])
return res
output = ['honda', 'jeep', audi']
and
def duplicate():
for c in all_cars['cars']:
if c['name'] == 'audi':
pass
else:
res = print(c['name'])
return res
output - ['honda', 'jeep']
I am trying to find matching values in both dictionaries, using list comprehension, then return non-matching values only.
Solution: Using 'in' rather than '==' operator, I was able to compare values between both lists and skip duplicates.
def duplicate():
for c in all_cars['cars']:
if c['name'] in [c['users']for c in owners['users']]:
pass
else:
res = print(c['name'])
return res
To answer the question in your title, you can conditionally add elements during a list comprehension using the syntax [x for y in z if y == a], where y == a is any condition you need - if the condition evaluates to True, then the element y will be added to the list, otherwise it will not.
I would just keep a dictionary of all of the owner data together:
ownerData = { "Shaft" : {
"carMake" : "Audi",
"carModel" : "A8",
"year" : "2015" },
"JamesBond" : {
"carMake" : "Aston",
"carModel" : "DB8",
"year" : "2012" },
"JeffBezos" : {
"carMake" : "Honda",
"carModel" : "Accord"
"year" : "1989"}
}
Now you can loop through and query it something like this:
for o in ownerData:
if "Audi" in o["carMake"]:
print("Owner %s drives a %s %s %s" % (o, o["year"], o["carMake"], o["carModel"]))
Should output:
"Owner Shaft drives a 2015 Audi A8"
This way you can expand your data set for owners without creating multiple lists.
OK, based on your feedback on the solution above, here is how I would tackle your problem. Drop your common items into lists and then use "set" to print out the diff.
all_cars = {'total_count': 3,'cars': [{'name': 'audi','model': 'S7'},
{'name': 'honda', 'model': 'accord'},{'name': 'jeep', 'model': 'wrangler'} ]}
owners = {'users':[{'owner': 'Nick', 'car': 'audi'},{'owner': 'Jim',
'car': 'ford'},{'owner': 'Mike', 'car': 'mercedes'} ]}
allCarList = []
ownerCarList = []
for auto in all_cars['cars']:
thisCar = auto['name']
if thisCar not in allCarList:
allCarList.append(thisCar)
for o in owners['users']:
thisCar = o['car']
if thisCar not in ownerCarList:
ownerCarList.append(thisCar)
diff = list(set(allCarList) - set(ownerCarList))
print(diff)
I put this in and ran it and came up with this output:
['jeep', 'honda']
Hope that helps!

Python Dictionary: Key with two values possible?

For a test program I'm crawling a webpage. I'd like to crawl all activites for specifid ID´s which are associated to the respective cities.
For example, my initial code:
RegionIDArray = {522: "London", 4745: "London", 2718: "London", 3487: "Tokio"}
Im now wondering if its possible to sum up all IDs (values) which are related to e.g. London into one key:
RegionIDArray = {522, 4745, 2718: "London}
If I´m trying this, I get no results
My full code so far
RegionIDArray = {522: "London", 4745: "London", 2718: "London", 3487: "Tokio"}
for reg in RegionIDArray:
r = requests.get("https://www.getyourguide.de/-l" +str(reg) +"/")
soup = BeautifulSoup(r.content, "lxml")
g_data = soup.find_all("span", {"class": "intro-title"})
for item in g_data:
POI_final = (str(item.text))
end_final = ("POI: " + POI_final)
if end_final not in already_printed:
print(end_final)
already_printed.add(end_final)
Is there any smart way.Appreciate any feedback.
You can do this in 2 steps:
Create a dictionary mapping locations to list of IDs.
Reverse this dictionary, taking care to ensure your keys are hashable.
The first step is optimally processed via collections.defaultdict.
For the second step, you can use either tuple or frozenset. I opt for the latter since it is not clear that ordering is relevant.
from collections import defaultdict
RegionIDArray = {522: "London", 4745: "London", 2718: "London", 3487: "Tokio"}
d = defaultdict(list)
for k, v in RegionIDArray.items():
d[v].append(k)
res = {frozenset(v): k for k, v in d.items()}
print(res)
{frozenset({522, 2718, 4745}): 'London',
frozenset({3487}): 'Tokio'}
You can use itertools.groupby:
import itertools
RegionIDArray = {522: "London", 4745: "London", 2718: "London", 3487: "Tokio"}
new_results = {tuple(c for c, _ in b):a for a, b in itertools.groupby(sorted(RegionIDArray.items(), key=lambda x:x[-1]), key=lambda x:x[-1])}
Output:
{(3487,): 'Tokio', (4745, 522, 2718): 'London'}
What you can do is make a reverse lookup table from the values to all working keys, like so:
def reverse(ids):
table = {}
for key in ids:
if ids[key] not in table:
table[ids[key]] = []
table[ids[key]].append(key)
return table

Nested Dicts with Lists

I want to build a dict with lists as values, which contain dicts with lists as values:
xml_dict = {
'Spain':['La Palma':[2929, ..], 'Fuerteventura':[5733, ..]],
'Turkey':['Antalya':[16483, ..], 'Izmir':[2927, ...]]
... }
My starting basis is a list with dicts:
self.db_data_list = [
{'land': 'Spain', 'giatahotelcode': 2929, 'zielgebiet_abweichung': 'La Palma'},
{'land': 'Spain', 'giatahotelcode': 5733, 'zielgebiet_abweichung': 'Fuerteventura'},
{'land': 'Turkey', 'giatahotelcode': 16483, 'zielgebiet_abweichung': 'Antalya'},
{'land': 'Turkey', 'giatahotelcode': 2927, 'zielgebiet_abweichung': 'Izmir'}
And here's my code so far:
zg_giata_dict = dict()
country_zg_dict = dict()
xml_dict = dict()
countries = [ value for data_dict in self.db_data_list for key, value in data_dict.iteritems() if key == 'land' ]
countries = set(countries)
zgs = [ value for data_dict in self.db_data_list for key, value in data_dict.iteritems() if key == 'zielgebiet_abweichung' ]
zgs = set(zgs)
for data_dict in self.db_data_list:
for country in countries:
if data_dict['land'] == country:
country_zg_dict.setdefault(country, []).append(data_dict['zielgebiet_abweichung'])
country_zg_dict[country] = list(set(country_zg_dict[country]))
for data_dict in self.db_data_list:
for zg in zgs:
if data_dict['zielgebiet_abweichung'] == zg:
zg_giata_dict.setdefault(zg, []).append(data_dict['giatahotelcode'])
for country, zg_list in country_zg_dict.iteritems():
for zg, giata_list in zg_giata_dict.iteritems():
if zg in zg_list:
xml_dict.setdefault(country, []).append(giata_list)
Output xml_dict:
{'Spain': [[2929, ...], [5733, ...]], 'Turkey': [[16483, ...], [2927, ...]], ... }
My output is not bad at all - but I miss the values from zielgebiet_abweichung (self.db_data_list) in my xml_dict. I have no idea how to manage that. Any ideas?
I think what you actually need is a dictionary with dictionaries as values, where those inner dictionaries have lists as values.
for hotel in self.db_data_list:
land = hotel['land']
town = hotel['zielgebiet_abweichung']
code = hotel['giatahotelcode']
if land in all_data:
if town in all_data[land]:
all_data[land][town].append(code)
else:
all_data[land][town] = [code]
else:
all_data[land] = {town: [code]}
This returns:
{'Turkey': {'Antalya': [16483], 'Izmir': [2927]},
'Spain': {'Fuerteventura': [5733], 'La Palma': [2929]}}
You can also use setdefault to do the same thing:
all_data = {}
for hotel in self.db_data_list:
land = all_data.setdefault(hotel['land'], {})
town = land.setdefault(hotel['zielgebiet_abweichung'], [])
town.append(hotel['giatahotelcode'])

Categories

Resources