Reputation: 53
grade=['Ben Anderson',95,90,100,-1,'Mary Johnson',75,78,79,-5,'Michael Walter',80,68,0]
def convert_grades(lst):
a = []
b = []
for i in lst:
if isinstance(i,str):
c = 0
while lst[c] < 0 or lst[c] == []:
a = a + lst[c]
c = c + 1
b = b + a
return b
I want it return as
[['Ben Anderson',95,90,100],['Mary Johnson',75,78,79],['Michael Walter',80,68,0]]
but it return as []
I do not know what's going on. needs help.
Upvotes: 1
Views: 146
Reputation:
If you want a comprehension, this works:
grades=['Ben Anderson',95,90,100,-1,'Mary Johnson',75,78,79,-5,'Michael Walter',80,68,0]
eyes=[i for i, x in enumerate(grades)
if isinstance(x,str)]+[len(grades)+1] # index of the strings
LofSL=[(i,j) for i,j in zip(eyes[:-1],eyes[1:])] # slices for each list
LoL=[grades[t[0]:t[1]] for t in LofSL] # your list of lists
Or, if you want a dictionary:
DofL={grades[t[0]]:grades[t[0]+1:t[1]] for t in LofSL}
Upvotes: 0
Reputation: 103714
Just to be complete, you can treat this structure [str, int, int, str, int]
as a stack and pop from the left into the structure you wish:
grades=['Ben Anderson',95,90,100,-1,'Mary Johnson',75,78,79,-5,'Michael Walter',80,68,0]
converted_list=[]
while grades:
temp=[grades.pop(0)]
while grades and isinstance(grades[0],int):
temp.append(grades.pop(0))
converted_list.append(temp)
print converted_list
Prints:
[['Ben Anderson', 95, 90, 100, -1], ['Mary Johnson', 75, 78, 79, -5], ['Michael Walter', 80, 68, 0]]
You can use this same method to create a dictionary, which seems like a better data structure:
d={}
while grades:
name=grades.pop(0)
d[name]=[]
while grades and isinstance(grades[0],int):
d[name].append(grades.pop(0))
print d
Prints:
{'Mary Johnson': [75, 78, 79, -5], 'Michael Walter': [80, 68, 0], 'Ben Anderson': [95, 90, 100, -1]}
While this works, IMHO, F.J's answer is the most 'Pythonic'
Upvotes: 1
Reputation: 18418
Yet another answer (although I like F.J's answer more than mine) and a few comments (just suggestions):
#!/usr/bin/env python
def convert_grades1(lst):
a = []
b = []
index = 0
while index < len(lst):
if isinstance(lst[index], str):
b.append(lst[index])
index = index + 1
try:
while isinstance(lst[index], int):
if lst[index] > 0:
b.append(lst[index])
index += 1
except IndexError:
print "We're done"
finally:
a.append(b)
b = []
return a
if __name__ == "__main__":
grade=['Ben Anderson',95,90,100,-1,'Mary Johnson',75,78,79,-5,'Michael Walter',80,68,0]
print convert_grades1(grade)
1)
If you're "walking" lists (or parsing files or whatever...) with while loops think if you really need to re-start walking from the beginning in a nested loop. In your code:
for i in lst:
if isinstance(i,str):
c = 0
while lst[c] < 0 or lst[c] == []:
You start re-walking the whole list in the while (you do c=0
right before getting in it), even though you may have already processed a chunk of it in previous for "passes". I'm guessing you were thinking on i
being the index (which is not, i
gets the values of the items in the list). For indexes, use: for i in range(0, len(lst))
or for i, item in enumerate(lst)
2)
c = 0
while lst[c] < 0 or lst[c] == []:
There, lst[c]
is pointing to the fist item in the list (meaning, the string 'Ben Anderson'
), which is neither less than 0 nor an empty list so the while
loop is never going to be executed.
3)
It's usually considered "pythonic" having your code following the idea of "better being sorry than safe" so instead of if isinstance(i,str)
you can (in your example) try to parse an int and if it fails... well... then you can assume it's an string.
if isinstance(element, str):
#do stuff for string
else:
#do other stuff (for int)
Can be equivalent (in your case):
try:
int(element)
#do other stuff (for int)
except ValueError:
#do stuff for string
Be careful, because int("5")
won't throw any exception (even though "5"
is actually an str
). It'll give you an int
with a value of 5
.
4)
If you're a beginner, print
is your friend ;)
Upvotes: 1
Reputation: 27575
A suggestion with the same form of input and output:
def convert_grades(lst):
out = []
for element in grade:
if isinstance(element, str):
buf = [] # re-initializes 'buf' everytime there is a string
out.append(buf)
buf.append(element)
return out
Three main symptoms of non-pythonicity of the former code:
list.append
in this case);So, a yet more pythonic way, returning a dictionary instead of a list:
def convert_grades(lst):
out = {}
for element in grade:
if isinstance(element, str):
key = element
out[key] = []
else:
out[key].append(element) ## mind that this would raise an error if first element in lst is not string
return out
print convert_grades(grade)
Hope this helps!
Upvotes: 5
Reputation: 250881
try something like this:
grade=['Ben Anderson',95,90,100,-1,'Mary Johnson',75,78,79,-5,'Michael Walter',80,68,0]
def convert_grades(lst):
a = []
for i in lst:
if isinstance(i,str):
a.append([]) #if a string is found than append a [] to a
a[-1].append(i) #append i to the last element of a
elif i>=0:
a[-1].append(i) #if not string then just append i to the last element of a
return a
print(convert_grades(grade))
output:
[['Ben Anderson', 95, 90, 100], ['Mary Johnson', 75, 78, 79], ['Michael Walter', 80, 68, 0]]
Upvotes: 1
Reputation: 208405
I think itertools.groupby()
might be pretty applicable here:
from itertools import groupby
def convert_grades(lst):
key = lambda x: isinstance(x, int) and x < 0
return [list(g) for k, g in groupby(lst, key) if not k]
Result:
>>> convert_grades(['Ben Anderson',95,90,100,-1,'Mary Johnson',75,78,79,-5,'Michael Walter',80,68,0])
[['Ben Anderson', 95, 90, 100], ['Mary Johnson', 75, 78, 79], ['Michael Walter', 80, 68, 0]]
This works by creating a function key
that returns True
when a list entry should act as a separator, and False
when it shouldn't. By using this function in itertools.groupby()
we can create all the groups, and then we just need to filter out all of the separator values from the resulting iterable.
Upvotes: 3