user1598686
user1598686

Reputation: 53

why my code can not get the expected return?

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

Answers (6)

user648852
user648852

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

dawg
dawg

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

Savir
Savir

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

heltonbiker
heltonbiker

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:

  1. Use of arithmetic operators where someone already familiar with python's idioms would use type methods (list.append in this case);
  2. Inputs in a list where the obvious data type should be a dictionary (although it's not always possible to control how you get the data);
  3. And, the most severe symptom, the OUTPUT being a list of lists when it's actually begging a dictionary.

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

Ashwini Chaudhary
Ashwini Chaudhary

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

Andrew Clark
Andrew Clark

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

Related Questions