views:

213

answers:

3

Hello,

I am trying to learn Python lists. In this code I am trying to add a string s coming from a form to table row as long as the string is the same. When the string is different; the new string is written to the next column.

I could write the string from column 0 to column 1 but I had problems adding the same string on column 1 correctly.

The way it is; the script works only up to placing the different string to the next column.

I realize that this is not the right way of doing this.

I would appreciate any help. Thank you. I include the template too.

EDIT2

@JerseyMike: Thanks for your answer. I don't yet understand how AddString(str) works but trying it in IDLE I noticed that instead of adding the new string it replaces it with the new string. As I said, I have not yet studied how it works; but here is the result (I changed str to str1):

>>> def AddString(str1):
    try:
        idx = map(lambda (s, v): ('N', 'Y')[s == str1], L).index('Y')
    except:
        L.append((str1, 1))
    else: L[idx] = (str1, L[idx][1] + 1)


>>> L = []
>>> str1 = 'hello'
>>> AddString(str1)
>>> L
[('hello', 1)]
>>> AddString(str1)
>>> L
[('hello', 2)]
>>> 

EDIT

@JerseyMike:

Thanks, I am sorry, I realized the question was not clear. In this app; the user types in the same sentence; like practicing foreign language. So the input will be

Hello world
Hello world
Hello world

and if the user types in next "Hello universe" that will go to the next column:

Hello world  Hello Universe
Hello world
Hello world

and if the user keeps typing "Hello Universe" they should go under the same column

Hello world  Hello Universe
Hello world  Hello Universe
Hello world  Hello Universe
             Hello Universe
             Hello Universe

The list L containing this looks like this:

L = [
     ['Hello world', 'Hello Universe'], 
     ['Hello world', 'Hello Universe'], 
     ['Hello world', 'Hello Universe'], 
     ['',            'Hello Universe'], 
     ['',            'Hello Universe']
    ]

Initially the list is empty and I add the string s with L.append(s).

L = [
     ['Hello world'], 
     ['Hello world'], 
     ['Hello world'], 
    ]

If the last string s does not match the new input I create the new column with L[0].insert(1,s).

L = [
     ['Hello world', 'Hello Universe'], 
     ['Hello world'], 
     ['Hello world'], 
    ]             

Now I need to write under 'Hello Universe' This turned out to be difficult for me to figure out for several reasons. But now I think it may be better to append the new string s to the list before checking if it is same as the previous string. To simplify the list, assume L is like this:

L = [['A'], ['A'], ['A'], ['B']]

Now ['B'] needs to be inserted into L[0]. To do this I search the list to the left to find the last sub-list with 1 element (or something like this). I have not looked into how to search lists yet. Thanks again for the help.

END EDİT


class Test(webapp.RequestHandler):
    myList = []
    def get(self):        
#        del self.myList[:]        
        s = [self.request.get('sentence')]
        r = len(self.myList)

        if r == 0: 
            self.myList.append(s)
            htmlcode1 = HTML.table(self.myList)
            lastItem = s  
        else:   
            if len(self.myList[0]) == 1:
                lastItem = self.myList[r-1]        
                if s == lastItem:
                    self.myList.append(s)             
                    htmlcode1 = HTML.table(self.myList)                
                else:
                    s = self.request.get('sentence') 
                    self.myList[0].insert(1,s)       
                    htmlcode1 = HTML.table(self.myList)
            if len(self.myList[0]) == 2:                
                    self.myList[1].insert(1,s)
                    htmlcode1 = HTML.table(self.myList)
            elif len(self.myList[1]) == 2:             
                    self.myList[2].insert(1,s)
                    htmlcode1 = HTML.table(self.myList)

        template_values = {'htmlcode1': htmlcode1,
                           's': s,
                           'r': r,
                           'myList': self.myList,
#                           'myListLen': myListLen,
                           'lastItem': lastItem,
                          }

        path = os.path.join(os.path.dirname(__file__), 'test.mako')
        templ = Template(filename=path)
        self.response.out.write(templ.render(**template_values))    

Template

<html>
<head>
</head>
<body>

<p>| <a href="/delete">CLEAR</a> |</p>

<form action="/test" method="get">
<input name="sentence" type="text" size="30"><br />
      <input type="submit" value="Enter">
</form>

<p>${htmlcode1}</p>
<p>s: ${s}</p>
<p>r: ${r}</p>
<p>myList: ${myList}</p>
<p>lastItem: ${lastItem}</p>

</html>
</body>
A: 

If I were you, I wouldn't use nested lists. Instead, use a dictionary.

What you can do with a dictionary is map keys (the strings you want one row each for) to values (in this case, however many times that string occurs, or the number of columns in that row of the table).

So, you could do something a bit like this:

    def add_item(item, dict):
        if item in dict:
            dict[item] += 1
        else:
            dict[item] = 1

You can reference individual strings or rows in the table by using dict[string] and see how many columns that row has.

Also, using a dictionary is far more concise, memory efficient, and fast than using nested lists.

If you REALLY have to do this with nested lists, let me know and I'll try and help with that.

Rafe Kettler
@Rafe Kettler: Thanks. I will read about dictionaries and try this approach as well.
Zeynel
+2  A: 

If the order of the strings is important, a dictionary will be a problem. You are on the right path using lists, but I think you need more than just that. Honestly, I'm not clear on what your output will look like for different data sets. If I read your question correctly, is the following true?

Input:

Sentence 1
Sentence 3
Sentence 1
Sentence 1
Sentence 2
Sentence 2

Output after last line:

Sentence 1 | Sentence 3 | Sentence 2
Sentence 1 |            | Sentence 2
Sentence 1 |            |

If not, please rephrase your expected output with an example.


Ok, so it looks like my take on your problem was correct. That's a good start. ;)

I think we need to look at this from a different perspective. Currently, you are looking at this as a big list of all the data and maybe that's too much. What I see here is a list of tuples, where each tuple represents a column (string, count). The tuple will be what string should be in this column and how many of them should be there. So your example would end up looking like:

L = [("Hello World", 3), ("Hello Universe", 5)]

I know this isn't obvious as to how to deal with this data, but I think it's the right way to represent it internally. I'll give you some sample code to do some basic manipulations of this data type (others may have more efficient ways of doing the same thing).

Add a new String:

def AddString(str):
try:
    idx = map(lambda (s, v): ('N', 'Y')[s == str], L).index('Y')
except:
    L.append( (str, 1) )
else:
    L[idx] = (str, L[idx][1] + 1)

Print the inside of an HTML table:

def PrintChart():
try:
    max = reduce(lambda x, y: [x, y][x[1] < y[1]], L)[1]
except:
    print "<td></td>"
else:
    for i in range(max):
        print "<tr>"
        for (s, c) in L:
            if i+1 <= c:
                print "  <td" + s + "</td>"
            else:
                print "  <td></td>"
        print "</tr>"

So anyway, that is the way I'd do it.

JerseyMike
Thanks. I tried to clarify the question with an edit.
Zeynel
My code above assumes the existence of a global 'L' that holds the list of tuples.
JerseyMike
@JerseyMike: I tried `AddString(str)` and added the result above as EDIT2. Thanks. I will update as I understand how your snippet works.
Zeynel
A: 

Looks to me like you're trying to shoehorn everything into the one list, which is going to make your code hard to write. Something to note is that you're adding blank strings ('') to your list to pad it out, which is a bit of a warning sign that your data structure is wrong. Rather than do that, I'd use a dictionary keyed off either an id or the original text, store the user input as a list after that, then sort out how to display this in the display part of your code. Something like:

typing = {
    'Hello World': ['Hello World', 'Hello World', 'Hello World']
    'Hello Universe': ['Hello Universe', 'Hello Universe', 'Hello Universe', ...]
}

Bear in mind that dictionaries are unordered though, so you might need to either use a list to order them, or use sorteddict from the collections library.

Anthony Briggs