IndexError: list index out of range for CSV

Question:

I am currently working on an Inventory System, and have just developed code that allows me to add items to my .csv

My current csv file looks like this:

enter image description here

Here is my relevant code:

class CsvReader:
    def __init__(self): 
        self.result = []

    def make_dict_items(self):
        #fn = os.path.join('subdir', "Items2.csv")
        with open("Items2.csv") as fp:
            reader = csv.reader(fp)
            labels = next(reader, None)
            result = []
            for row in reader:
                row[0] = int(row[0])
                row[1] = float(row[1])
                row[2] = int(row[2])
                pairs = zip(labels, row)
                self.result.append(dict(pairs))

    def show_available(self):
        for item in self.result:
           print(item)
        
    def add_item(self):
       item_num = int(input("What is the items #?n"))
       price = float(input("What is the items price?n"))
       quant = int(input("What is the items quantity?n"))
       name = input("What is the items name?n")
       new_row = [item_num, price, quant, name]
       with open("Items2.csv", "a+") as  fp:
           reader = csv.reader(fp)
           fp.seek(0)
           labels = next(reader, None)
           writer = csv.writer(fp)
           new_record = dict(zip(labels, new_row))
           self.result.append(new_record)
           writer.writerow(new_record.values())

From what I understand, my add item is able to successfully input into my csv. For example, if I input 1, 2.99, 3, and Pumpkin into the inputs, and print(system.result), I can see the new dictionary in the list, and visually see it if I were to open the csv document.

What I dont understand is my current error I am getting. When I try to view my new item with my existing method that shows all items, I get an error:

row[0] = int(row[0])
IndexError: list index out of range

I understand this error has to do with trying to call elements in a list that don’t exist, but what seems to be the issue here. Why when I have the four items in my csv, the code works fine, but when I add a new row into the csv file, the code fails? Why does that specific line in make_dict_items not work when a 5th item is added, but works with the beginning four?

Code doesnt with with new inputted item

Asked By: py_coder1019

||

Answers:

Your CSV has blank lines, which create empty row. Check for this and skip it.

            for row in reader:
                if row:
                    row[0] = int(row[0])
                    row[1] = float(row[1])
                    row[2] = int(row[2])
                    pairs = zip(labels, row)
                    self.result.append(dict(pairs))
Answered By: Barmar

I would also suggest looking into something like dataclasses and dataclass-csv for this, just to align with best practices in Python and to also to streamline the task, and make it easier to work with the data.

Here are contents of Book.csv that I was testing with:

Item #,Price,Quantity,Name
1,5.99,1,Blueberry Muffin
2,6.99,2,Bannana-nut Muffin
3,7.99,3,Chocolate Chip Cookie
4,8.99,4,Oatmeal Cookie

For example, here’s a working sample I was able to test with. This works with pip install dataclass-csv to install the library beforehand.

from dataclasses import dataclass

import dataclass_csv


@dataclass
class MyClass:
    item_no: int
    price: float
    quantity: int
    name: str


with open('Book.csv') as f:
    reader = dataclass_csv.DataclassReader(f, MyClass)

    reader.map('Item #').to('item_no')
    reader.map('Price').to('price')
    reader.map('Name').to('name')
    reader.map('Quantity').to('quantity')

    for line in reader:
        print(line)

Notice here how the type annotations such as price: float indicate that the type of the field should be float for example.

The output of the above code is as follows:

MyClass(item_no=1, price=5.99, quantity=1, name='Blueberry Muffin')
MyClass(item_no=2, price=6.99, quantity=2, name='Bannana-nut Muffin')
MyClass(item_no=3, price=7.99, quantity=3, name='Chocolate Chip Cookie')
MyClass(item_no=4, price=8.99, quantity=4, name='Oatmeal Cookie')

A Simpler Approach

I’m planning to update my flagship library that I’ve been developing for a while now, dataclass-wizard, so that it adds proper CSV support — currently it does not handle CSV data at all. I’ve checked in a WIP branch to track my efforts so far, and hope to create a PR and release soon.

Usage should be mostly similar to above, but hopefully more streamlined in some aspects:

from dataclasses import dataclass
from typing import Annotated  # Python < 3.9: import from `typing_extensions` instead

from dataclass_wizard import Alias, CSVWizard


@dataclass
class Inventory(CSVWizard):
    item_no: Annotated[int, Alias('Item #', all=True)]
    price: float
    quantity: int
    name: str


# items is type `Container[Inventory]`
items = Inventory.from_csv_file('Book.csv')
print(items)

# add a new row to existing CSV file
new_item = Inventory(7, 12.99, quantity=10, name='Apple Strudel')
new_item.append_to_csv_file('Book.csv')

# copy data to a new file
items.to_csv_file('Book.New.csv')

Output is similar to above:

[Inventory(item_no=1, price=5.99, quantity=1, name='Blueberry Muffin'),
 Inventory(item_no=2, price=6.99, quantity=2, name='Bannana-nut Muffin'),
 Inventory(item_no=3, price=7.99, quantity=3, name='Chocolate Chip Cookie'),
 Inventory(item_no=4, price=8.99, quantity=4, name='Oatmeal Cookie')]
Answered By: rv.kvetch
Categories: questions Tags: , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.