Python: Read multiple lines from a file and make instances stored in an dictionary

Question:

My struggle:

Reading two lines and jumping over the third.

Then I want to store all the objects in a dictionary with the name as keys.

**** Ingredients.txt ****
Name1
ingredient1/ingredient2/ingredient3

Name2
ingredient1/ingredient2

Name3
...

class Foodset(object):
    def __init__(self, name):
        self.name = name
        self.ingredients = set([])

    def __str__(self):
        return str(self.name) + ' consits of: ' + ", ".join(str(e) for e in self.ingredients) + '.'

    def setIngredients(self, list):
        for i in list:
            self.ingredients.add(i)

def getMenu(file="ingredients.txt"):
    with open(file, 'r') as indata:
        menu = dict()
        for line in indata:
            tempString = str(line.rstrip('n'))
            menu[tempString] = Foodset(tempString)

I want to read the next line and store as ingredients and then skip the third line since it’s blank. Then repeat.

The problem I’m having with the for loop is that I can’t store two different lines in the same loop and then refer to the same object in order to use the setIngredients() method. What other ways can I read multiple lines inside each loop?

EDIT:
@Arpan came up with a quick solution to use indata.readlines() to make a list of every line and loop with steps of 3 while storing first and second value and skipping the third.

I just came up with an other solution using the readline() method 3 times inside a while loop. Using readline() is what I originally wanted.

def getMenu(menu="ingredients.txt"):
    with open(menu, "r") as indata:
        menu = dict()
        while True:
            name = indata.readline().strip('n')
            ingredientList = indata.readline().strip().split('/')
            if name == "":
                break
# here I just added a parameter that directly set the attribute "ingredients" inside the object.
            menu[name] = Foodset(name, ingredientList)
            indata.readline()
    return menu
    
Asked By: TheRawPerformer

||

Answers:

I will show you a way to parse your file. Building a dictionary from the dishes and their ingredients is the simple part, so I will leave that to you.

Demo file input.txt:

Name1
ingredient1/ingredient2/ingredient3

Name2
ingredient1/ingredient2

Name3
ingredient1/ingredient2/ingredient3

Code:

from itertools import izip_longest

with open('input.txt') as infile:
    for name, ingr, _ in izip_longest(*[infile]*3, fillvalue='n'):
        ingredients = ingr.split('/')
        print name.strip()
        for ing in ingredients:
            print ing.strip()
        print

Output:

Name1
ingredient1
ingredient2
ingredient3

Name2
ingredient1
ingredient2

Name3
ingredient1
ingredient2
ingredient3

Explanation:

infile is an iterator, which is passed three times to izip_longest, so each iteration izip_longest will call next on the same iterator three times. I use an empty line as the fillvalue argument in case your file does not end with an empty line.

Splitting the string with the ingredients by the '/' character will give you a list of ingredients.

There’s more explanation/information and also some alternatives on how to read multiple lines from a file at once here.

Answered By: timgeb

Try something like this.

with open(file, 'r') as indata:
    lines = indata.readlines()
menu = dict()
for i in xrange(0, len(lines), 3):
    name = lines[i].rstrip('n')
    ingredients = lines[i+1].rstrip('n').split('/')
    f = Foodset(name)
    f.setIngredients(ingredients)
    menu[name] = f

For python 3.x use range instead of xrange.

Answered By: Arpan

You can read three lines at once using itertools.islice

import itertools
with open('ingredients.txt') as f:
    while True:
        next_three_lines = list(itertools.islice(f, 3))
        if not next_three_lines:
            break
        else:
            print next_three_lines

In your case this will print

['Name1n', 'ingredient1/ingredient2/ingredient3n', 'n']
['Name2n', 'ingredient1/ingredient2n', 'n']
['Name3n', '...']

Then you can change the print line to rstrip('n') and use the first two elements of each next_three_lines to build your object:

tempString = str(next_three_lines[0].rstrip('n'))
menu[tempString] = Foodset(tempString)
menu[tempString].setIngredients(next_three_lines[1].rstrip('n').split('/')
Answered By: Francesco