Question

I have a text file with entries that look like this :

JohnDoe

Assignment 9  
Reading: NO  
header: NO  
HW: NO  
Solutions: 0 
show: NO  
Journals: NO  
free: NO  
Finished: NO  
Quiz: 0  
Done     
Assignment 3  
E-book: NO  
HW: NO  
Readings: NO  
Show: 0  
Journal: NO 
Study: NO  
Test: NO  
Finished: NO  
Quiz: 0  
Done

This is a small sample. The file has several students in it. Each student has two assignments under their name and they only pass if the line that starts with "Finished" in each assignment reads "Finished: YES". All of the data under each assignment is disorganized, but somewhere under each assignment a line will say "Finished: YES (or NO)" I need a way to read the file and say whether or not any of the students have passed. So far, I have

def get_entries( file ):
with open( "dicrete.txt.rtf", 'rt') as file:
    for line in file:
        if "Finished" in line:
            finished, answer = line.split(':')
            yield finished, answer

# dict takes a sequence of  `(key, value)` pairs and turns in into a dict
print dict(get_entries( file ))

I can only get this code to return a single entry (the first "Finished" it reads as key and "YES or NO" as value, which is what I want, but I want it to return Every line in the file that that starts with "Finished". So the sample data I provided I want to return a dict with 2 entries {Finished:"NO" , Finished:"NO"}

Was it helpful?

Solution

Dictionaries can only store one mapping per key. So, you can never have a dictionary that has two different entries for the same key.

Consider using a list of two-tuples instead, like [("Finished", "NO"), ("Finished", "NO")].

OTHER TIPS

Sounds like you need a better data model! Let's look at that, shall we?

Let's define an Assignment class that we can call with all the lines of text between Assignment: # and Finished: YES/NO.

class Assignment(object):
    def __init__(self, id, *args, **kwargs):
        self.id = id
        for key,val in kwargs.items():
            setattr(self, key.lower(), val)
        finished = getattr(self, 'finished', None)
        if finished is None:
            raise AttributeError("All assignments must have a 'finished' value")
        else:
            self.finished = True if finished.lower() == "yes" else False

    @classmethod
    def from_string(cls, s):
        """Builds an Assignment object from a string

        a = Assignment.from_string('''Assignment: 1\nAttributes: Go Here\nFinished: yes''')
        >>> a.id
        1
        >>> a.finished
        True"""
        d = dict()
        id = None
        for line in s.splitlines():
            key,*val = map(str.strip, line.split(":"))
            val = ' '.join(val) or None
            if key.lower().startswith('assignment'):
                id = int(key.split()[-1])
                continue
            d[key.lower()] = val
        if id is not None:
            return cls(id, **d)
        else:
            raise ValueError("No 'Assignment' field in string {}".format(s))

Once you have your model, you'll need to parse your input. Luckily this is actually pretty simple.

def splitlineson(s, sentinel):
    """splits an iterable of strings into a newline separated string beginning with each sentinel.

    >>> s = ["Garbage", "lines", "SENT$", "first", "group", "SENT$", "second", "group"]
    >>> splitlineson(s, "SENT$")
    iter("SENT$\nfirst\ngroup",
         "SENT$\nsecond\ngroup")"""

    lines = []
    for line in s:
        if line.lower().strip().startswith(sentinel.lower()):
            if any((sentinel.lower() in line.lower() for line in lines)):
                yield "\n".join(lines)
            lines = [line.strip()]
        else:
            if line:
                lines.append(line.strip())
    yield "\n".join(lines)

with open('path/to/textfile.txt') as inf:
    assignments = splitlineson(inf, "assignment ")

assignment_list = [Assignment.from_string(a) for a in assignments]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top