Sort names as well as scores - python

I'm a computing student who posted a question here the other day about helping me with my function to sort scores in order and I got some great help and it now works but I would also like it to sort the names according to the scores (so if James gets 10 then it prints "James 10". Right now what is happening is that the scores are sorting and printing to the screen properly but the names are just printing in the order that they are entered. I've tried this:
def sortlist():
global scorelist, namelist, hss
namelist = []
scorelist = []
hs = open("hstname.txt", "r")
namelist = hs.read().splitlines()
hss = open("hstscore.txt","r")
for line in hss:
scorelist.append(int(line))
switched = True
while switched:
switched = False
for i in range(len(scorelist)-1):
for j in range(len(namelist)-1):
if scorelist[i] < scorelist[i+1]:
scorelist[i],scorelist[i+1] = scorelist[i+1],scorelist[i]
namelist[j],namelist[j+1] = namelist[j+1],namelist[j]
switched = True
The score part works fine and it took me ages to get it and I'm not allowed to use a pre defined function like .sort(). Can anyone offer any help/advice? Or if you can see what I'm doing wrong then can you offer a solution? I can't work this out for the life of me

You don't need to use nested loops in order to go through both lists.
Since you should be manipulating the two lists in exactly the same way, you should only be using one for loop, and using the i variable to index into both lists.
If yoy turn this:
for i in range(len(scorelist)-1):
for j in range(len(namelist)-1):
if scorelist[i] < scorelist[i+1]:
scorelist[i],scorelist[i+1] = scorelist[i+1],scorelist[i]
namelist[j],namelist[j+1] = namelist[j+1],namelist[j]
switched = True
into this:
for i in range(len(scorelist)-1):
if scorelist[i] < scorelist[i+1]:
scorelist[i],scorelist[i+1] = scorelist[i+1],scorelist[i]
namelist[i],namelist[i+1] = namelist[i+1],namelist[i]
switched = True
then you should get both lists sorted as you want them.
The only time when this will cause an error is if your two lists are of different lengths. If namelist is somehow shorter than scorelist then this code will throw an exception. You could guard against that by checking before your sort routine that
len(scorelist) == len(namelist)

Imagine, you have a function that returns a single sorted list e.g. here's an implementation of Quicksort algorithm in Python:
def qsorted(L):
return L and (qsorted([x for x in L[1:] if x < L[0]]) + # lesser items
[L[0]] + # pivot
qsorted([x for x in L[1:] if x >= L[0]])) # greater or equal
Then you could use it to sort your scorelist:
qsorted_scorelist = qsorted(scorelist)
To sort namelist according to the order of scorelist; you could use Schwartzian transform:
qsorted_namelist = [name for score, name in qsorted(zip(scorelist, namelist))]
Note that the same function qsorted() is used in both case: to sort the scorelist along and to sort both lists together. You should try to extract common functionality into separate functions instead of modifying your sorting algorithm inplace for a slighty different task.
To test that the result is correct; you could use the builtin sorted() function:
sorted_namelist = [name for score, name in sorted(zip(scorelist, namelist))]

Related

Append number to list if string matches list name

I am trying to write a Python program as following:
list_a = []
list_b = []
list_c = []
listName = str(input('insert list name: '))
listValue = int(input('insert value: '))
listName.append(listValue)
But unfortunately "listName.append()" won't work.
Using only IF functions as in:
if listName == 'list_a':
list_a.append(listValue)
is impractical because I am actually working with 600+ lists...
Is there any other way I could do to make something like this work properly??
Help is much appreciated!
Thank you very much in advance!!
When you're tempted to use variable names to hold data — like the names of stocks — you should almost certainly be using a dictionary with the data as keys. You can still pre-populate the dictionary if you want to (although you don't have to).
You can change your existing to code to something like:
# all the valid names
names = ['list_a', 'list_b', 'list_c']
# pre-populate a dictionary with them
names = {name:[] for name in names}
# later you can add data to the arrays:
listName = str(input('insert list name: '))
listValue = int(input('insert value: '))
# append value
names[listName].append(listValue)
With this, all your data is in one structure. It will be easy to loop through it, make summary statistics like sums/averages act. Having a dictionary with 600 keys is no big deal, but having 600 individual variables is a recipe for a mess.
This will raise a key error if you try to add a name that doesn't exists, which you can catch in a try block if that's a possibility.
Keep your lists in a dict. You could initialize your dict from a file, db, etc.
lists = {"GOOG": []}
listName = str(input('insert list name: '))
listValue = int(input('insert value: '))
lists.setdefault(listName,[]).append(listValue)
# Just a little output to see what you've got in there...
for list_name, list_value in lists.items():
print(list_name, list_value)
So, following Mark Meyer's and MSlimmer's suggestions above, I am using a dictionary of lists to simplify data input, which has made this section of my program work flawlessly (thanks again, guys!).
However, I am experiencing problems with the next section of my code (which was working before when I had it all as lists haha). I have a dictionary as below:
names = {'list_a':[5, 7, -3], 'list_b':[10, 12, -10, -10]}
Now, I have to add up all positives and all negatives to each list. I want it to have the following result:
names_positives = {'list_a': 12, 'list_b': 22}
names_negatives = {'list_a': -3, 'list_b': -20}
I have tried three different ways, but none of them worked:
## first try:
names_positives = sum(a for a in names.values() if a > 0)
## second try:
names_positives = []
names_positives.append(a for a in names.values() if compras > 0)
## third try:
names_positives = dict(zip(names.keys(), [[sum(a)] for a in names.values() if compra > 0]))
To be honest, I have no idea how to proceed -- I am getting errors due to mixing strings and integers in those lines, and I am not being able to work some way around this problem... Any help is much appreciated. It could result in a dictionary, in a list or even in only the total sum (as in the first try, under no better circumstances).
Cheers!
You can try this one:
I just added one line for your code to work.
list_a = ['a']
list_b = []
list_c = []
listName = str(input('insert list name: '))
listValue = int(input('insert value: '))
eval(listName).append(listValue)
the eval function evaluates the string into an actual python expression. It should be noted however that there are security issues regarding the use of eval. But for the exact question that you were trying to answer, this would be the easiest way that wouldn't require much refactoring of the existing code.

Instead of repeating a loop multiple times 'merge' into one

Set-up
I'm scraping apartment ads using Scrapy. For certain housing characteristics, I loop over the elements of a list BC I obtain per ad. If the characteristic is in the list I assign a 'yes' and if not a 'no'. E.g.
for x in BC:
if 'Terraza' in x:
terrace = 'yes'
break
else:
terrace = 'no'
For each 'yes-no' characteristic I have a copy of the above loop.
Problem
Besides looping over the elements of the list, I'd like to loop over the characteristics themselves. I.e. I'd like to 'merge' all the loops per characteristic into one loop.
I've tried the following (my actual bcl does contain multiple elements):
found = False
bcl = ['Terraza']
for x in l: # l is a list of strings containing housing characteristics
for y in bcl:
if y in x:
y = 'yes'
found = True
break
else:
y = 'no'
if found:
break
terrace = Terrazza
but this loop does not create the variable Terrazza. I'm not sure I can solve this with globals.
How do I make this loop work?
Depending on the wanted result, you may take a different approach. In a case like this, I tend to use a more functional style of coding. I am not sure if this is what you intend, but I think you could do it this way:
list_characteristics = ['Terraza', "Swimming pool"] # sample ad text - list of strings
bcl = ["test1", "test2", "Terraza", "test3"] # sample checklist - list of strings
def check_characteristics(checklist, adlist):
list_of_found_items = []
for characteristic in list_characteristics:
print("characteristic:", characteristic)
for item in bcl:
print("item:", item)
if item in characteristic:
list_of_found_items.append(item)
return list_of_found_items
result_list = check_characteristics(bcl, list_characteristics)
print("This ad has the following characteristics:", result_list)
Using the code above, you have a function that takes two lists of strings and lists all the items found. In case you one want to know if there is at least one of those, you can use this other function as a faster, short-circuit, way:
list_characteristics = ['Terraza', "Swimming pool"] # ad text - list of strings
bcl = ["test1", "test2", "Terraza", "test3"] # checklist - list of strings
def has_any_characteristic(checklist, adlist):
for characteristic in list_characteristics:
for item in bcl:
if item in characteristic:
return True
return False
result = has_any_characteristic(bcl, list_characteristics)
print("This ad has at least one of the wanted characteristics?", result)
Seems like a lot of code, but you code it once and then you use it whenever you need it, in a clean and easy to read way, IMHO. The definition of these two functions can even be placed in a separate module that you import where needed. So, in the main code, you will only need to use one line to call the function. Each function allows to answer a simple question in a way that is easy to understand, as presented in the print()statements in both code samples above.
Your problem isn't in merging loops but probably in breaking from outer loop. You can break out of a top loop by raising custom exception and then trying to catch it. Take a look at this peace of code:
bcl = ['Terraza']
class FoundCharacteristicException(Exception):
pass
for x in li:
try:
for y in bcl:
if y in x:
raise FoundCharacteristicException
except FoundCharacteristicException:
break

Custom sort method in Python is not sorting list properly

I'm a student in a Computing class and we have to write a program which contains file handling and a sort. I've got the file handling done and I wrote out my sort (it's a simple sort) but it doesn't sort the list. My code is this:
namelist = []
scorelist = []
hs = open("hst.txt", "r")
namelist = hs.read().splitlines()
hss = open("hstscore.txt","r")
for line in hss:
scorelist.append(int(line))
scorelength = len(scorelist)
for i in range(scorelength):
for j in range(scorelength + 1):
if scorelist[i] > scorelist[j]:
temp = scorelist[i]
scorelist[i] = scorelist[j]
scorelist[j] = temp
return scorelist
I've not been doing Python for very long so I know the code may not be efficient but I really don't want to use a completely different method for sorting it and we're not allowed to use .sort() or .sorted() since we have to write our own sort function. Is there something I'm doing wrong?
def super_simple_sort(my_list):
switched = True
while switched:
switched = False
for i in range(len(my_list)-1):
if my_list[i] > my_list[i+1]:
my_list[i],my_list[i+1] = my_list[i+1],my_list[i]
switched = True
super_simple_sort(some_list)
print some_list
is a very simple sorting implementation ... that is equivelent to yours but takes advantage of some things to speed it up (we only need one for loop, and we only need to repeat as long as the list is out of order, also python doesnt require a temp var for swapping values)
since its changing the actual array values you actually dont even need to return

Working with lists - Python

i ran into a little logic problem and trying to figure it out.
my case is as follows:
i have a list of items each item represents a Group
i need to create a set of nested groups,
so, for example:
myGroups = ["head", "neck", "arms", "legs"]
i need to get them to be represented like this:
(if you can imaging a folder structure)
head
|_> neck
|_> arms
|_>legs
and so on until i hit the last element.
what i thought would work (but don't know really how to advance here) is:
def createVNTgroups(self, groupsData):
for i in range(len(groupsData)):
print groupsData[i]
for q in range(1, len(groupsData)):
print groupsData[q]
but in this case, i am running over same elements in 'i' that i already took with 'q'.
could someone give me a hint?
thanks in advance!
If I understood well, you want a nested structure. For this case, you can use a recursive function:
myGroups = ["head", "neck", "arms", "legs"]
def createVNTgroups(alist):
temp = alist[:] # needed because lists are mutable
first = [temp.pop(0)] # extract first element from list
if temp: # if the list still contains more items,
second = [createVNTgroups(temp)] # do it recursively
return first + second # returning the second object attached to the first.
else: # Otherwise,
return first # return the last element
print createVNTgroups(myGroups)
this produces a nested list:
['head', ['neck', ['arms', ['legs']]]]
Is that what you were looking for?
>>> m
['head', 'neck', 'arms', 'legs']
>>> reduce(lambda x,y:[x,y][::-1] if x!=y else [x], m[::-1],m[-1])
['head', ['neck', ['arms', ['legs']]]]

Nested for loop to search 2 lists

Using: Python 2.4
Currently, I have a nested for loop that iterates over 2 lists and makes a match based on two elements that exists on both lists. Once a match has been found, it the element from the r120Final list and puts in a new list called "r120Delta":
for r120item in r120Final:
for spectraItem in spectraFinal:
if(str(spectraItem[0]) == r120item[2].strip()) and (str(spectraItem[25]) == r120item[10]):
r120Delta.append(r120item)
break
The problem is that this is SO SLOW and the lists aren't that deep. The R120 is about 64,000 lines and the Spectra is about 150,000 lines.
The r120Final list is a nested array and it looks like so:
r120Final[0] = [['xxx','xxx','12345','xxx','xxx','xxx','xxx','xxx','xxx','xxx','234567']]
...
r120Final[n] = [['xxx','xxx','99999','xxx','xxx','xxx','xxx','xxx','xxx','xxx','678901']]
The spectraFinal list is essentially the same, a nested array and it looks like so:
spectraFinal[0] = [['12345','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','234567']]
...
spectraFinal[0] = [['99999','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','xxx','678901']]
Finally, the reason for the "r120Delta" is so that way I can then do a list differential between r120Final and r120Delta and retrieve r120 data elements that were NOT matched. This is the function I defined for this very task, and again, slow:
def listDiff( diffList, completeList ):
returnList = []
for completeItem in completeList:
if not completeItem in diffList:
returnList.append(completeItem)
return returnList
Basically, I'm knowledgeable in Python but by no means an expert. I'm looking for some experts to show me how to speed this up. Any help is appreciated!
spectra_set = set((str(spectraItem[0]), str(spectraItem[25])) for spectraItem in spectraFinal)
returnList = []
for r120item in r120Final:
if (r120item[2].strip(), r120item[10]) not in spectra_set:
returnList.append(r120item)
This will add all items that didn't match to the returnList.
You can do it in one line (if you really want) as
returnList = [r120item for r120item in r120Final
if (r120item[2].strip(), r120item[10]) not in
set((str(spectraItem[0]), str(spectraItem[25]))
for spectraItem in spectraFinal)]
If you need the full spectraItem:
spectra_dict = dict(((str(spectraItem[0]), str(spectraItem[25])), spectraItem) for spectraItem in spectraFinal)
returnList = []
for r120item in r120Final:
key = (r120item[2].strip(), r120item[10])
if key not in spectra_dict:
returnList.append(r120item)
else:
return_item = some_function_of(r120item, spectra_dict[key])
returnList.append(return_item)

Categories

Resources