0

I have a CSV file keeping track of people's score in a game. I read it with csv.DictReader(). How do I create a dictionary with the keys being the names and the value being the scores?

CSV file example:

name,Game_1, Game_2, Game_3
James,3,7,4
Charles,2,3,8
Bob,6,2,4

Note: the game names can change, and amount of games too.

Code Example:

# Dictionary to load scores into
score_sheet = {}

# Read CSV file into memory
with open("scores.txt") as scores_file:
    reader = csv.DictReader(scores_file)

    list_of_games = reader.fieldnames[1:]

    # Load each row seperately
    for row in reader:
        for game in list_of_games:
            score_sheet[row[0]] = {row[game]:row[game[1]} 
        
# Experiment 1
    # Load each row separately
    for row in reader:
        score_sheet[row[0]] = row[1]
# Experiment 2
for row, value in reader:
            print(row, value)

Desired output:

score_sheet = {
    'James':{'Game_1':'3'}, {'Game_2':'7'}, {'Game_3':'4'}
    'Charles':{'Game_1':'2'}, {'Game_2':'3'}, {'Game_3':'8'}
    'Bob':{'Game_1':'6'}, {'Game_2':'2'}, {'Game_3':'4'}
    # Optionally:
    # name:[{game:score}, {game:score}, {game:score}]
}

Errors:

score_sheet[row[0]] = {row[game]:row[game[1]} 
#           ^^^^^^ KeyError: 0

score_sheet[row[0]] = row[1]
#                     ^^^^^^ KeyError: 1
for row, value in reader:
            print(row, value)
#
# for row, value in reader:
#     ^^^^^^^^^^
# ValueError: too many values to unpack (expected 2)

How do I access specific keys or values for dictionaries from a CSV file? Please help me understand what I am missing or misunderstanding.

2
  • Maybe first use print() (and print(type(...)), print(len(...)), etc.) to see which part of code is executed and what you really have in variables. It is called "print debugging" and it helps to see what code is really doing. Commented Aug 17 at 22:08
  • maybe better keep it as JSON file - it doesn't need so many work. Commented Aug 17 at 22:23

2 Answers 2

2

You can't create

'James': {'Game_1':'3'}, {'Game_2':'7'}, {'Game_3':'4'}

(if you don't mean tuple of dicts)

It has to be:

list of dicts

'James': [ {'Game_1':'3'}, {'Game_2':'7'}, {'Game_3':'4'} ]

tuple of dicts

'James': ( {'Game_1':'3'}, {'Game_2':'7'}, {'Game_3':'4'} )

or one dict

'James': {'Game_1':'3', 'Game_2':'7', 'Game_3':'4'}

You use csv.DictReader() so every row is already dict and you should use row['name'] instead of row[0]

You should first create empty list score_sheet[row['name']] = [] and later use for-loop to append() dictionares with {game: row[game]}

for row in reader:

    score_sheet['name'] = []

    for game in list_of_games:
        score_sheet[row[0]].append( {game:row[game]} )

And it should give

score_sheet = {
    'James': [ {'Game_1':'3'}, {'Game_2':'7'}, {'Game_3':'4'} ],
    # ...
}

You may also use list/tuple comprehension in place of normal for-loop.

    # list comprehension
    score_sheet[row['name']] = [{game:row[game]} for game in list_of_games]

    # tuple comprehension
    score_sheet[row['name']] = tuple({game:row[game]} for game in list_of_games)

If you want it as one dict

score_sheet = {
    'James': {'Game_1':'3', 'Game_2':'7', 'Game_3':'4'},
    # ...
}

then first you have to create empty dict score_sheet[row['name']] = {} and later use for-loop to move games to this dictionary score_sheet[row['name']][game] = row[game]

    for row in reader:

        score_sheet[row['name']] = {}

        #print(row)
        for game in list_of_games:
            score_sheet[row['name']][game] = row[game]

You may also use dict comprehension

    # dict comprehension
    score_sheet[row['name']] = {game:row[game] for game in list_of_games}

Full working code used for tests.

I used io.StringIO only to emulate file-like object in memory - so everyonce can it copy and run.

# 
text = '''name,Game_1, Game_2, Game_3
James,3,7,4
Charles,2,3,8
Bob,6,2,4
'''

import csv
import io

# --- version with list/tuple ---

score_sheet = {}

with io.StringIO(text) as scores_file:
#with open("scores.txt") as scores_file:
    reader = csv.DictReader(scores_file)

    list_of_games = reader.fieldnames[1:]

    for row in reader:

        #score_sheet[row['name']] = []

        #print(row)
        #for game in list_of_games:
        #    score_sheet[row['name']].append( {game:row[game]} )

        # if you need `tuple of dicts` instead of `list of dicts`
        #score_sheet[row['name']] = tuple(score_sheet[row['name']])

        # ---

        # list comprehension
        score_sheet[row['name']] = [{game:row[game]} for game in list_of_games]

        # tuple comprehension
        score_sheet[row['name']] = tuple({game:row[game]} for game in list_of_games)

print(score_sheet)

# --- version with dict ---

score_sheet = {}

with io.StringIO(text) as scores_file:
#with open("scores.txt") as scores_file:
    reader = csv.DictReader(scores_file)

    list_of_games = reader.fieldnames[1:]

    for row in reader:

        #score_sheet[row['name']] = {}

        #print(row)
        #for game in list_of_games:
        #    score_sheet[row['name']][game] = row[game]

        # ---

        # dict comprehension
        score_sheet[row['name']] = {game:row[game] for game in list_of_games}

print(score_sheet)

EDIT:

Because csv.DictReader() gives dictonary in every row (as @KelluBundy also mentioned in comment) so you can also remove name from row to get dictionary only with games and it doesn't need to create new dict for games. And it doesn't need list_of_games

with io.StringIO(text) as scores_file:
#with open("scores.txt") as scores_file:
    reader = csv.DictReader(scores_file)

    #list_of_games = reader.fieldnames[1:]  # no need it

    for row in reader:

        name = row.pop('name')   # get `name` and remove it from `row`
        score_sheet[name] = row
Sign up to request clarification or add additional context in comments.

4 Comments

This is absolutely phenomenal, thank you! I understand it a lot better now! One question I want to ask is: How is row['name'] able to get its values? I understand how (in theory) row[0] could get to that column, but how does it get there by entering a string? Maybe me coming from C is messing this understanding up.
when you use csv.reader() then row is a list and you can use row[0] but when you use csv.DictReader() then it creates dictionary in row and you can use column name to get value. Simply use print(row) and print( type(row) ) to see what you have in variable
if my answer helped you then you can mark it as accepted. It will be also information for others that problem was solved.
That helps too :) Thank you, have a nice day!
1

All you need is a simple function which convert your row such as:

{'name': 'James', 'Game_1': '3', ' Game_2': '7', ' Game_3': '4'}

into tuple of key and value:

('James', {'Game_1': 3, ' Game_2': 7, ' Game_3': 4})

That function is

def normalize(row: dict):
    name = row.pop("name")
    row = {key: int(value) for key, value in row.items()}
    return name, row

How to use it

# Read CSV file into memory
with open("scores.txt") as scores_file:
    reader = csv.DictReader(scores_file, skipinitialspace=True)
    score_sheet = dict(normalize(row) for row in reader)

Note: Use skipinitialspace=True to remove empty spaces in front of the data.

How does it work

The rows look like this:

{'name': 'James', 'Game_1': '3', 'Game_2': '7', 'Game_3': '4'}
{'name': 'Charles', 'Game_1': '2', 'Game_2': '3', 'Game_3': '8'}
{'name': 'Bob', 'Game_1': '6', 'Game_2': '2', 'Game_3': '4'}

The normalize() function convert them. Note the conversion of string to int:

('James', {'Game_1': 3, 'Game_2': 7, 'Game_3': 4})
('Charles', {'Game_1': 2, 'Game_2': 3, 'Game_3': 8})
('Bob', {'Game_1': 6, 'Game_2': 2, 'Game_3': 4})

Finally, dict() convert them into:

{
  "James": {
    "Game_1": 3,
    "Game_2": 7,
    "Game_3": 4
  },
  "Charles": {
    "Game_1": 2,
    "Game_2": 3,
    "Game_3": 8
  },
  "Bob": {
    "Game_1": 6,
    "Game_2": 2,
    "Game_3": 4
  }
}

1 Comment

Wow, this is also a phenomenal solution! Thank you for your explanation, this helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.