pythoncsvwriter

Why do I get a KeyError when using writerow() but not when using print()?


I'm designing a program that works as designed to take a csv file in the form of last name, first name, Harry Potter house and write it to another csv file in the form of first name, last name, house. It works fine when I print it locally to my terminal (when the part commented out is used), but throws the following error when I run it as is.

Traceback (most recent call last):
  File "/workspaces/107595329/scourgify/scourgify.py", line 26, in <module>
    writer.writerow(row[first], row[last], row[house])
KeyError: ' Hannah'

Hannah being the first of the first names in the file. How can I re-write the writer.writerow() line(line 26) to write first, last, and house to my after.csv file? Code here:

import csv
import sys

students = []

try:
    if len(sys.argv) < 3:
        print("Too few command line arguments")
        sys.exit(1)
    if len(sys.argv) > 3:
        print("Too many command line arguments")
        sys.exit(1)
    if not sys.argv[1].endswith(".csv") or not sys.argv[2].endswith(".csv"):
        print("Not a CSV file")
        sys.exit(1)
    elif sys.argv[1].endswith(".csv") and sys.argv[2].endswith(".csv"):
        with open(sys.argv[1]) as file:
            reader = csv.DictReader(file)
            for row in reader:
                students.append({"name": row["name"], "house": row["house"]})
        with open(sys.argv[2], "w") as after:
            writer = csv.DictWriter(after, fieldnames = ["first, last, house"])
            for row in students:
                house = row["house"]
                last, first = row["name"].split(",")
                writer.writerow(row[first], row[last], row[house])
                #print(first, last, house)

except FileNotFoundError:
    print("Could not read " + sys.argv[1])
    sys.exit(1)

Solution

  • Some notes about the OP code:

            with open(sys.argv[1]) as file:
                reader = csv.DictReader(file)
                for row in reader:
                    students.append({"name": row["name"], "house": row["house"]})
                # "row" is already the dictionary being formed here, so
                # students.append(row) is all that is needed
                # Could also do the following and skip the for loop
                # students = list(reader)
    
            with open(sys.argv[2], "w") as after:
                # fieldnames is wrong.
                # fieldnames = ['first','last','house'] is what is needed
                writer = csv.DictWriter(after, fieldnames = ["first, last, house"])
                for row in students:
                    house = row["house"]
                    last, first = row["name"].split(",")
                    # DictWriter.writerow takes a single dictionary as a parameter.
                    # correct would be:
                    # writer.writerow({'first':first,'last':last,'house':house})
                    writer.writerow(row[first], row[last], row[house])
    

    Frankly, DictReader is overkill for this task. Open both files at once and re-write each line as it is read with using the standard reader/writer objects.

    An input file wasn't provided, so given the following from the code description:

    input.csv

    name,house
    "Potter, Harry",Gryffindor
    "Weasley, Ron",Gryffindor
    "Malfoy, Draco",Slytherin
    "Lovegood, Luna",Ravenclaw
    

    This code will process as needed:

    test.py

    import csv
    
    # Note: newline='' is a requirement for the csv module when opening files.
    #       Without ends up writing \r\r\n line endings on Windows.
    with (open('input.csv', newline='') as file,
          open('output.csv', 'w', newline='') as after):
    
        reader = csv.reader(file)
        next(reader) # skip header
        writer = csv.writer(after)
        writer.writerow(['first', 'last', 'house']) # write new header
        for name, house in reader:
            last, first = name.split(', ')
            writer.writerow([first, last, house])
    

    output.csv

    first,last,house
    Harry,Potter,Gryffindor
    Ron,Weasley,Gryffindor
    Draco,Malfoy,Slytherin
    Luna,Lovegood,Ravenclaw