I'm building an app where you can search for objects in a database (let's assume the objects you search for are persons). What I want to do is to group related objects, for example married couples. In other words, if two people share the same last name, we assume that they are married (not a great example, but you get the idea). The last name is the only thing that identifies two people as married.
In the search results I want to display the married couples next to each other, and all the other persons by themselves.
Let's say you search for "John", this is what I want:
John Smith - Jane Smith
John Adams - Nancy Adams
John Washington
John Andersson
John Ryan
Each name is then a link to that person's profile page.
What I have right now is a function that finds all pairs, and returns a list of tuples, where each tuple is a pair. The problem is that on the search results, every name that is in a pair is listed twice.
I do a query for the search query (Person.objects.filter(name__contains="John")), and the result of that query is sent to the match function. I then send both the original queryset and the match function result to the template.
I guess I could just exclude every person that the match function finds a match for, but I don't know, but is that the most efficient solution?
Edit:
As I wrote in a comment, the actual strings that I want to match are not identical. To quote myself:
In fact, the strings I want to match are not identical, instead they
look more like this: "foo2(bar13)" - "foo2(bar14)". That is, if two
strings have the same foo id (2), and if the bar id is an odd number
(13), then its match is the bar id + 1 (14). I have a regular
expression to find these matches
First get your objects sorted by last name:
def keyfun(p):
return p.name.split()[-1]
persons = sorted(Person.objects.all(), key = keyfun)
Then use groupby:
from itertools import groupby
for lname, persons in groupby(persons, keyfun):
print ' - '.join(p.name for p in persons)
Update Yes, this solution works for your new requirement too. All you need is a stable way to generate keys for each item, and replace the body of the keyfun with it:
from re import findall
def keyfun(p):
v1, v2 = findall(p.name, '\d+')
tot = int(v1) + int(v2) % 2
return tot
Your description for how to generate the key for each item is not clear enough, although you should be able to figure it out yourself with the above example.
Related
I have a database where data is organized by a number that is unique, and a name that isn't unique. for example:
NumCOL: NameCOL:
1 Jay
2 Joel
3 Joey
4 Joel
Could I use a filter and get statement to grab names where the number is equal to a certain number? Let's say I have a form that lets a user pick a number from the database and the user picks the number 2.
num = request.POST.get('FormNumber') #num = 2
name = Database.objects.filter(NumCOL=num).get('NameCOL')
return HttpResponse(name)
Can something like this be done? I want to grab the name wherever the user selects based on their number. Based on the code I should get a response Joel.
Thanks for your help!
name = Database.objects.get(NumCOL=num)
#name = Database.objects.filter(NumCOL=num)
return HttpResponse(name.NameCOL)
As pointed by daniel in the comments and by metmirr in his answer, you don't need to do it like this. The following works just fine.
name = Database.objects.get(NumCOL=num)
return HttpResponse(name.NameCOL)
Retrieving all the fields of a single model does not add any overhead whatsoever to the query. Get is used to retrieve a single row and not a single column.
To retrieve a single column, you can do:
name = Database.objects.filter(NumCOL=num).values('NameCol')
To retrieve a single cell you can do
name = Database.objects.filter(NumCOL=num).values_list('NameCol', flat=True)
As a side note, by convention we name the model with the first letter in upper case and fields are all lower case.
I have an interesting question that very well may be answered with "Do it another way."
I have a function that iterates through a list with a for loop. I call the function within itself on certain parameters, and keep iterating through the list from the point it was at. The issue is I would like to be able to jump out of the recursive call back into the top function but keep track of how far I went in the list and go on from there.
Basically I want something like this:
def iterate_list(listA)
dictA = {}
for pos,item in enumerate(listA):
if item == 1:
dictA[pos] = iterate_list(listA[pos])
#At this point I want to go back to for loop (like continue does) except I want
#to be at the pos I was at when I left the sub function
continue #? Don't think continue is what I want but its the closest thing I could
#find so I left it in for now
elif item == 2:
return dictA
else:
dictA[pos] = item
return dictA
dictFinal = iterate_list(original_list)
So the end result is a dictionary of whatever is in the list (integers in this example but not always) except for some points when the key points to a sub dictionary. This is a simplified version of the code were I took out all the extra code that gets the keys and values (that bit works I tested it extensively) so what I'm putting in the dictionary here looks a little silly (but simulates what I'm doing well enough). Thanks for the help.
edit: A little more detail on the input and output as requested. The input is a list of strings that are mostly written as word : word, the output is the first word as the key, the second as the value for the dictionary. The parsing of the strings code is written and works. But there are some areas of repeated keys so I want those to go into a sub dictionary. So for example
Input = [Name: Bob, ID: 12345, Age: 99, Job: Dentist, Patient Name: John, Patient ID: 321, Patient Name: Susan, Patient ID: 666, Patient Name: Lucy, Patient ID: 087, Employees: 5, Address: 233 Main St, Phone: 555-5555]
Output = {Name : Bob, ID : 12345, Age : 99, Job : Dentist, Patient1 : {Patient Name : John, Patient ID : 321}, Patient2 : {Patient Name : Susan, Patient ID : 666}, Patient3 : {Patient Name : Lucy, Patient ID : 087}, Employees : 5, Address : 233 Main St, Phone : 555-5555}
If that makes sense. Let me know if more detail is needed.
One simple answer would be to use an iterator. If you pass an iterator to a recursive call and it consumes some elements, when the recursive call returns, you'll continue where it left off:
def function(iterable):
iterable = iter(iterable):
for thing in iterable:
if some_condition(thing):
function(iterable)
continue
# The next iteration of the loop will use
# the first item the recursive call didn't.
This might not cut it, though; for example, you might need to go back a position or two in the list, and most Python iterators don't support that. In that case, you could write an iterator that allows you to unconsume elements, or you could iterate with an explicit index and put the index you stopped at into the return value.
It seems like you are parsing input list. List elements are tokens and grammar is simple but syntactic analysis is better to implemented with more (recursive) functions, depending on grammar definition.
From your example, BNF seems like:
<patient> ::= <patient-id> <patient-name> # Patient has 2 fields
<doctor-data> ::= Name | ID | Age | Job # Personal doctor data is combination of these data
<doctor> ::= <doctor-data> <patient>* # Doctor data is personal data and some patients
<hospital_data> ::= Employees | Address | Phone # Hospital data fields
<hospital> ::= <doctor>* <hospital_data> # Hospital has doctors and additional data
I have this working but I'm sure there must be a better method
The context is a movie/television app so there are titles (movies/tv) and people who act in each, many to many relationship.
I have a "titlepeople" model with information such as:
id, people_fk, title_fk, role_title
On movies where a cast member has alot of roles I need to display their information like:
Tom Hanks: Gardener, Police Man #1, Another Role #4
Is there anyway I can optimize the below way of doing this so the code isn't so lengthy?
cast_unique = list()
for person in cast:
#if not in the unique list, add them
if person.people not in [p.people for p in cast_unique]:
cast_unique.append(person)
else:
# if in the list, append the role information
if person.role_title:
for c in cast_unique:
if c.people == person.people:
# append role info
c.role_title = '{0} / {1}'.format(c.role_title, person.role_title)
Thanks
You should change cast_unique to be a dictionary where you use the cast member as the key. This will allow much greater performance because you won't have to iterate the cast_unique iterable.
Also, your use a list comprehension in the if person.people not in [p.people for p in cast_unique]: requires an entire list to be create of people for every iteration for the test; which, could use a lot of memory plus there's no way to short circuit the list comprehension when a match occurs. Still a dictionary is a much better data type to use for this situation.
cast_unique = {}
for person in cast:
if person.people not in cast_unique:
cast_unique[person.people] = person
else:
cast_unique[person.people].role_title = '{0} / {1}'.format(cast_unique[person.people].role_title, person.role_title)
I have a list of names and addresses organized in the following format:
Mr and Mrs Jane Doe
Candycane Lane
Magic Meadows, SC
I have several blocks of data written like this, and I want to be able to alphabetize each block by the last name (Doe, in this case). After doing some digging, the best I can reckon is that I need to make a "List of lists" and then use the last name as a key by which to alphabetize the block. However, given by freshness to python and lack of Google skills, the closest I could find was this. I'm confused as to converting each block to a list and then slicing it; I can't seem to find a way to do this and still be able to alphabetize properly. Any and all guidance is greatly appreciated.
If I understood correctly, what you want basically is to sort values by "some computation done on the value", in this case the extracted last name.
For that, use the key keyword argument to .sort() or sorted():
def my_key_function(original_name):
## do something to extract the last name, for example:
try:
return original_name.split(',')[1].strip()
except IndexError:
return original_name
my_sorted_values = sorted(my_original_values, key=my_key_function)
The only requirement is that your "key" function is deterministic, i.e. always return the same output for each given input.
You might also want to sort by last name and then first name: in this case, just return a tuple (last, first): if last si the same for two given items, first will be used to further sort the two.
Update
For your specific case, this function should do the trick:
def my_key_function(original_name):
return original_name.splitlines()[0].split()[-1]
Assuming you already have the data in a list
l = ['Mr and Mrs Jane Smith\nCandycane Lane\nMagic Meadows, SC',
'Mr and Mrs Jane Doe\nCandycane Lane\nMagic Meadows, SC',
'Mr and Mrs Jane Atkins\nCandycane Lane\nMagic Meadows, SC']
You can specify the key to sort on.
l.sort(key=lambda x: x.split('\n')[0].split(' ')[-1])
In this case, get the last word (.split(' ')[-1]) on the first line (.split('\n')[0])
you want to make a new list where each entry is a tuple containing the sort key you want and the whole thing. Sort that list and then get the second component of each entry in the sort:
def get_sort_name (address):
name, address, city = address.split('\n')
return (name.split(' ')[-1] , address) # last item of first line & whole thing as tulle
keyed_list = map (get_sort_name, addresses)
keyed_list.sort()
sorted_addresses = [item[1] for item in keyed_list]
Thi could be more compact using lambdas of course but its better to be readable :)
This is a homework question, I got the basics down, but I can't seem to find the correct method of searching two parallel arrays.
Original Question: Design a program that has two parallel arrays: a String array named people that is initialized with the names of seven people, and a String array named phoneNumbers that is initialized with your friends' phone numbers. The program should allow the user to enter a person's name (or part of a person's name). It should then search for that person in the people array. If the person is found, it should get that person's phone number from the phoneNumbers array and display it. If the person is not found, program should display a message indicating so.
My current code:
# create main
def main():
# take in name or part of persons name
person = raw_input("Who are you looking for? \n> ")
# convert string to all lowercase for easier searching
person = person.lower()
# run people search with the "person" as the parameters
peopleSearch(person)
# create module to search the people list
def peopleSearch(person):
# create list with the names of the people
people = ["john",
"tom",
"buddy",
"bob",
"sam",
"timmy",
"ames"]
# create list with the phone numbers, indexes are corresponding with the names
# people[0] is phoneNumbers[0] etc.
phoneNumbers = ["5503942",
"9543029",
"5438439",
"5403922",
"8764532",
"8659392",
"9203940"]
Now, my entire problem begins here. How do I conduct a search (or partial search) on a name, and return the index of the persons name in the people array and print the phone number accordingly?
Update: I added this to the bottom of the code in order to conduct the search.
lookup = dict(zip(people, phoneNumbers))
if person in lookup:
print "Name: ", person ," \nPhone:", lookup[person]
But this only works for full matches, I tried using this to get a partial match.
[x for x in enumerate(people) if person in x[1]]
But when I search it on 'tim' for example, it returns [(5, 'timmy')]. How do I get that index of 5 and apply it in print phoneNumbers[the index returned from the search]?
Update 2: Finally got it to work perfectly. Used this code:
# conduct a search for the person in the people list
search = [x for x in enumerate(people) if person in x[1]]
# for each person that matches the "search", print the name and phone
for index, person in search:
# print name and phone of each person that matches search
print "Name: ", person , "\nPhone: ", phoneNumbers[index]
# if there is nothing that matches the search
if not search:
# display message saying no matches
print "No matches."
Since this is homework, I'll refrain from giving the code outright.
You can create a dict that works as a lookup table with the name as the key and the phone number as its value.
Creating the lookup table:
You can easily convert the parallel arrays into a dict using dict() and zip(). Something along the lines of:
lookup = dict(zip(people, phoneNumbers))
To see how that works, have a look at this example:
>>> people = ["john", "jacob", "bob"]
>>> phoneNumbers = ["5503942", "8659392", "8659392"]
>>> zip(people, phoneNumbers)
[('john', '5503942'), ('jacob', '8659392'), ('bob', '8659392')]
>>> dict(zip(people, phoneNumbers))
{'jacob': '8659392', 'bob': '8659392', 'john': '5503942'}
Finding if a person exist:
You can quickly figure out if a person (key) exist in the lookup table using:
if name in lookup:
# ... phone number will be lookup[name]
List of people whose name matches substring:
This answer should put you on the right track.
And of course, if the search returns an empty list there are no matching names and you can display an appropriate message.
Alternative suggestion
Another approach is to search the list directly and obtain the index of matches which you can then use to retrieve the phone number.
I'll offer you this example and leave it up to you to expand it into a viable solution.
>>> people = ["john", "jacob", "bob", "jacklyn", "cojack", "samantha"]
>>> [x for x in enumerate(people) if "jac" in x[1]]
[(1, 'jacob'), (3, 'jacklyn'), (4, 'cojack')]
If you hit a snag along the way, share what you've done and we'll be glad to assist.
Good luck, and have fun.
Response to updated question
Note that I've provided two alternative solutions, one using a dict as a lookup table and another searching the list directly. Your updates indicate you're trying to mix both solutions together, which is not necessary.
If you need to search through all the names for substring matches, you might be better off with the second solution (searching the listdirectly). The code example I provided returns a list (since there may be more than one name that contain that substring), with each item being a tuple of (index, name). You'll need to iterate throught the list and extract the index and name. You can then use the index to retrieve the phone number.
To avoid just giving you the solution, here's related example:
>>> people = ["john", "jacob", "bob", "jacklyn", "cojack", "samantha"]
>>> matches = [x for x in enumerate(people) if "jac" in x[1]]
>>> for index, name in matches:
... print index, name
...
1 jacob
3 jacklyn
4 cojack
>>> matches = [x for x in enumerate(people) if "doesnotexist" in x[1]]
>>> if not matches:
... print "no matches"
...
no matches
You might want to look here for the answer to How do I ... return the index of the persons name in the people array.