Linking two lists based on a common value and - python

I am new to Python 2.7 and I want the 1st column as the key column in employees and it has to check on dept 1st column and generate results.
Employees comes from a text file and dept comes from a database. I tried a lot but didn't get an easy answer. What is wrong with my code?
**Inputs :**
employees=['1','peter','london']
employees=['2','conor','london']
employees=['3','ciara','london']
employees=['4','rix','london']
dept=['1','account']
dept=['2','developer']
dept=['3','hr']
**Expected Output :**
results=['1','peter','london','account']
results=['2','conor','london','developer']
results=['3','ciara','london','hr']
results=['4','rix','london',null]

your input makes no sense. Each line overwrites the previous one data-wise. Here it seems that the digits (as string) are the keys, and some default action must be done when no info is found in dept.
To keep the spirit, just create 2 dictionaries, then use dictionary comprehension to generate the result:
employees = dict()
dept = dict()
employees['1'] = ['peter','london']
employees['2'] = ['conor','london']
employees['3'] = ['ciara','london']
employees['4'] = ['rix','london']
dept['1']=['account']
dept['2']=['developer']
dept['3']=['hr']
result = {k:v+dept.get(k,[None]) for k,v in employees.items()}
print(result)
which yields a dictionary with all the info. Note that null is None in python:
{'1': ['peter', 'london', 'account'], '4': ['rix', 'london', None], '3': ['ciara', 'london', 'hr'], '2': ['conor', 'london', 'developer']}

You could go for a class. Consider this:
class Employee:
def __init__(self, number, name, location, dept):
self.number = str(number)
self.name = name
self.location = location
self.dept = dept
def data(self):
return [self.number,
self.name,
self.location,
self.dept]
peter = Employee(1, 'peter', 'london', 'account')
print(peter.data())
['1', 'peter', 'london', 'account']
>>>

Related

Class objects (instances) into dictionary in Python

I would like to create multiple instances of the Product Class and convert each instance as a new item in a dictionary.
I'm not pretty sure how to append the new object and its attribute values to the dictionary
This is my class:
class Product():
def __init__(self, id, name, descr):
self.name = name
self.id = id
self.descr = descr
This is the part that creates the Product objects and inserts them into the dictionary:
addProductToDict(id, name, descr, product_dict):
new_product = Product(id, name, descr)
# insert new product instance into dict
product_dict <--- new_product ### pseudo code
return product_dict
product_dict = {}
while True:
print(" Give the products id, name and description")
id = input("Give id: ")
name = input("Give name: ")
descr = input("Give descr: ")
product_dict = addProductToDict(id, name, descr, product_dict)
Desired dictionary format:
my_dict = {'1': {'id': '1', 'name': 'TestName1', 'descr': 'TestDescription1'}, '2': {'id': '2', 'name': 'TestName2', 'descr': 'TestDescription2'}, '3': {'id': '3', 'name': 'TestName3', 'descr': 'TestDescription3'}}
Given your desired output, I have modified my answer.
pprint(vars(new_product))
class Product():
def __init__(self, id, name, descr):
self.name = name
self.id= id
self.descr= descr
product_dict = {}
new_product = Product(1, 'Test Name', 'Test Description')
product_dict = pprint(vars(new_product))
This will give you the desired format but I think you will have issues if you have more than one item in your dictionary.
Perhaps you want to store them in a list instead of a dictionary, unless you have a key for each object
products = []
products.append(Product(1, 'Test Name', 'Test Description'))
edit
so, if you have a key
products = {}
_id = 1
products[_id] = Product(_id, 'Test Name', 'Test Description')

How to receive any combination of an object's attributes and return the matching objects from a list of objects in Python?

I am sorry if this was answered before, but I could not find any answer for this problem at all.
Let's say I have this class and list of objects:
def Person:
def __init__(self, name, country, age):
self.name = name
self.country = country
self.age = age
persons = [Person('Tom', 'USA', 20), Person('Matt', 'UK', 19), Person('Matt', 'USA', 20)]
Now I would like the user to search for a person by entering any combination of attribute values and I want to return the objects that have all these values exclusively. For example, if the user enters: 'Matt', 'USA' and no age, I want the program to return the third person only who's Matt and is from the USA and not return all three objects because all of them have some of the entered combination of attribute values.
My implementation currently uses an if statement with the or operator which would return all the objects since the usage of or would return all the objects if one statement is True, which is what I am trying to solve.
Thanks in advance.
You can use a list comprehension for the task. And the if condition should check if the value is None else check in the list.
class Person:
def __init__(self, name, country, age):
self.name = name
self.country = country
self.age = age
def __repr__(self):
return "[{},{},{}]".format(name, country, str(age))
persons = [Person('Tom', 'USA', 20), Person('Matt', 'UK', 19), Person('Matt', 'USA', 20)]
name = "Matt"
country = "USA"
age = None
result = [
p for p in persons
if (name == None or p.name == name) and
(country == None or p.country == country) and
(age == None or p.age == age)
]
print(result) #[[Matt,USA,None]]

Converting Nonetype into String

I have this code which returns a Nonetype, when I thought it would return a string. And I have been trying to find solutions on stack, but I am yet to find one.
Here is the code:
members = ['Alex', 'Danny', 'Kieran', 'Zoe', 'Caroline']
visitors = ['Scott', 'Helen', 'Raj', 'Danny']
def check_group(members, visitors):
for person in visitors:
if person in members:
print(f"Member present: {person}")
break
else:
print("No members visited")
output: Member present: Danny
You have forgottent a sin when typing members line 2.
Also, your code won't return anythings, just print the value you asked it to print.
You need to have a return statement.
So if this is part of a function then it should be something like this
def find():
members = ['Alex', 'Danny', 'Kieran', 'Zoe', 'Caroline'] visitors = ['Scott', 'Helen', 'Raj', 'Danny']
for person in visitor:
if person in member:
print(f"Member present: {person}")
retrun person
else:
print("No members visited")
return None

Consolidating row data from DB into a list of dicts

I'm reading data from a SELECT statement of SQLite. Date comes in the following form:
ID|Phone|Email|Status|Role
Multiple rows may be returned for the same ID, Phone, or Email. And for a given row, either Phone or Email can be empty/NULL. However, for the same ID, it's always the same value for Status and the same for Role. for example:
1|1234567892|a#email.com| active |typeA
2|3434567893|b#email.com| active |typeB
2|3434567893|c#email.com| active |typeB
3|5664567891|d#email.com|inactive|typeC
3|7942367891|d#email.com|inactive|typeC
4|5342234233| NULL | active |typeD
5| NULL |e#email.com| active |typeD
These data are returned as a list by Sqlite3, let's call it results. I need to go through them and reorganize the data to construct another list structure in Python. The final list basically consolidates the data for each ID, such that:
Each item of the final list is a dict, one for each unique ID in results. In other words, multiple rows for the same ID will be merged.
Each dict contains these keys: 'id', 'phones', 'emails', 'types', 'role', 'status'.
'phones' and 'emails' are lists, and contains zero or more items, but no duplicates.
'types' is also a list, and contains either 'phone' or 'email' or both, but no duplicates.
The order of dicts in the final list does not matter.
So far I have come up this:
processed = {}
for r in results:
if r['ID'] in processed:
p_data = processed[r['ID']]
if r['Phone']:
p_data['phones'].add(r['Phone'])
p_data['types'].add('phone')
if r['Email']:
p_data['emails'].add(r['Email'])
p_data['types'].add('email')
else:
p_data = {'id': r['ID'], 'status': r['Status'], 'role': r['Role']}
if r['Phone']:
p_data['phones'] = set([r['Phone']])
p_data.setdefault('types', set).add('phone')
if r['Email']:
p_data['emails'] = set([r['Email']])
p_data.setdefault('types', set).add('email')
processed[r['ID']] = p_data
consolidated = list(processed.values())
I wonder if there is a faster and/or more concise way to do this.
EDIT:
A final detail: I would prefer to have 'phones', 'emails', and 'types' in each dict as list instead of set. The reason is that I need to dump consolidated into JSON, and JSON does not allow set.
When faced with something like this I usually use:
processed = collections.defaultdict(lambda:{'phone':set(),'email':set(),'status':None,'type':set()})
and then something like:
for r in results:
for field in ['Phone','Email']:
if r[field]:
processed[r['ID']][field.lower()].add(r[field])
processed[r['ID']]['type'].add(field.lower())
Finally, you can dump it into a dictionary or a list:
a_list = processed.items()
a_dict = dict(a_list)
Regarding the JSON problem with sets, you can either convert the sets to lists right before serializing or write a custom encoder (very useful!). Here is an example of one I have for dates extended to handle sets:
class JSONDateTimeEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
return int(time.mktime(obj.timetuple()))
elif isinstance(ojb, set):
return list(obj)
try:
return json.JSONEncoder.default(self, obj)
except:
return str(obj)
and to use it:
json.dumps(a_list,sort_keys=True, indent=2, cls =JSONDateTimeEncoder)
I assume results is a 2d list:
print results
#[['1', '1234567892', 'a#email.com', ' active ', 'typeA'],
#['2', '3434567893', 'b#email.com', ' active ', 'typeB'],
#['2', '3434567893', 'c#email.com', ' active ', 'typeB'],
#['3', '5664567891', 'd#email.com', 'inactive', 'typeC'],
#['3', '7942367891', 'd#email.com', 'inactive', 'typeC'],
#['4', '5342234233', ' NULL ', ' active ', 'typeD'],
#['5', ' NULL ', 'e#email.com', ' active ', 'typeD']]
Now we group this list by id:
from itertools import groupby
data_grouped = [ (k,list(v)) for k,v in groupby( sorted(results, key=lambda x:x[0]) , lambda x : x[0] )]
# make list of column names (should correspond to results). These will be dict keys
names = [ 'id', 'phone','email', 'status', 'roll' ]
ID_info = { g[0]: {names[i]: list(list( map( set, zip(*g[1] )))[i]) for i in range( len(names))} for g in data_grouped }
Now for the types:
for k in ID_info:
email = [ i for i in ID_info[k]['email'] if i.strip() != 'NULL' and i != '']
phone = [ i for i in ID_info[k]['phone'] if i.strip() != 'NULL' and i != '']
if email and phone:
ID_info[k]['types'] = [ 'phone', 'email' ]
elif email and not phone:
ID_info[k]['types'] = ['email']
elif phone and not email:
ID_info[k]['types'] = ['phone']
else:
ID_info[k]['types'] = []
# project
ID_info[k]['id'] = ID_info[k]['id'][0]
ID_info[k]['roll'] = ID_info[k]['roll'][0]
ID_info[k]['status'] = ID_info[k]['status'][0]
And what you asked for (a list of dicts) is returned by ID_info.values()

Store path to dictionary value

How might one store the path to a value in a dict of dicts? For instance, we can easily store the path to the name value in a variable name_field:
person = {}
person['name'] = 'Jeff Atwood'
person['address'] = {}
person['address']['street'] = 'Main Street'
person['address']['zip'] = '12345'
person['address']['city'] = 'Miami'
# Get name
name_field = 'name'
print( person[name_field] )
How might the path to the city value be stored?
# Get city
city_field = ['address', 'city']
print( person[city_field] ) // Obviously won't work!
You can do:
path = ('address', 'city')
lookup = person
for key in path:
lookup = lookup[key]
print lookup
# gives: Miami
This will raise a KeyError if a part of the path does not exist.
It will also work if path consists of one value, such as ('name',).
You can use reduce function to do this
print reduce(lambda x, y: x[y], city_field, person)
Output
Miami
An alternative that uses Simeon's (and Unubtu's deleted answer) is to create your own dict class that defines an extra method:
class mydict(dict):
def lookup(self, *args):
tmp = self
for field in args:
tmp = tmp[field]
return tmp
person = mydict()
person['name'] = 'Jeff Atwood'
person['address'] = {}
person['address']['street'] = 'Main Street'
person['address']['zip'] = '12345'
person['address']['city'] = 'Miami'
print(person.lookup('address', 'city'))
print(person.lookup('name'))
print(person.lookup('city'))
which results in:
Miami
Jeff Atwood
Traceback (most recent call last):
File "look.py", line 17, in <module>
print(person.lookup('city'))
File "look.py", line 5, in lookup
tmp = tmp[field]
KeyError: 'city'
You can shorten the loop per the suggestion of thefourtheye. If you want to be really fancy, you can probably override private methods like __get__ to allow for a case like person['address', 'city'], but then things may become tricky.
Here's another way - behaves exactly the same as Simeon Visser's
from operator import itemgetter
pget = lambda map, path: reduce(lambda x,p: itemgetter(p)(x), path, map)
With your example data:
person = {
'name': 'Jeff Atwood',
'address': {
'street': 'Main Street',
'zip': '12345',
'city': 'Miami',
},
}
pget(person, ('address', 'zip')) # Prints '12345'
pget(person, ('name',)) # Prints 'Jeff Atwood'
pget(person, ('nope',)) # Raises KeyError

Categories

Resources