2

I have a csv file and Im trying to create a nested dictionary that looks like this:

contacts = {"Tom": {"name": "Tom Techie",
                    "phone": "123 123546",
                    "email": "[email protected]",
                    "skype": "skypenick"},

            "Mike": {"name": "Mike Mechanic",
                     "phone": "000 123546",
                     "email": "[email protected]",
                     "skype": "-Mike-M-"}}

etc

And this is what I have written:

file = open("csv","r")
d = {}
for i in file:

    f = i.strip()
    x = f.split(";")

    if x[4] != "":
        d.update({x[0] : {"name":x[1],
                      "phone":x[2],
                      "email":x[3],
                      "skype":x[4]}})

    else:
        d.update ({x[0] : {"name": x[1],
                       "phone": x[2],
                       "email": x[3]}})

However it prints the dict as a normal dictionary with the updates as keys when they should be like stated above.

EDIT: First lines of the csv:

key;name;phone;email;skype
Tom;Tom Techie;123 123546;[email protected];skypenick
Mike;Mike Mechanic;000 123456;[email protected];-Mike-M-
Archie;Archie Architect;050 987654;archie@archie
3
  • 4
    Your question has insufficient infomration to a) run the code, b) reproduce the problem, c) help you. Commented Oct 24, 2018 at 18:47
  • 4
    You should at least provide the first few lines of the csv file Commented Oct 24, 2018 at 18:54
  • 1
    Edited first lines of cv to opening post Commented Oct 24, 2018 at 19:06

4 Answers 4

2

You can use pd.read_csv() and to_dict():

import pandas as pd

contacts = pd.read_csv('test.csv', sep=';').set_index('key').to_dict(orient='index')

Yields:

{'Tom': {'name': 'Tom Techie', 'phone': '123 123546', 'email': '[email protected]', 'skype': 'skypenick'}, 'Mike': {'name': 'Mike Mechanic', 'phone': '000 123456', 'email': '[email protected]', 'skype': '-Mike-M-'}, 'Archie': {'name': 'Archie Architect', 'phone': '050 987654', 'email': 'archie@archie', 'skype': nan}}
Sign up to request clarification or add additional context in comments.

Comments

1

I like the pandas answer, but if you don't want a 3rd party library, use the built-in csv module:

import csv
from pprint import pprint

D = {}
with open('csv',newline='') as f:
    r = csv.DictReader(f,delimiter=';')
    for line in r:
        name = line['key']
        del line['key']
        D[name] = dict(line)

pprint(D)

Output:

{'Archie': {'email': 'archie@archie',
            'name': 'Archie Architect',
            'phone': '050 987654',
            'skype': None},
 'Mike': {'email': '[email protected]',
          'name': 'Mike Mechanic',
          'phone': '000 123456',
          'skype': '-Mike-M-'},
 'Tom': {'email': '[email protected]',
         'name': 'Tom Techie',
         'phone': '123 123546',
         'skype': 'skypenick'}}

Comments

0

You can use zip() to achieve your goal:

file = """key;name;phone;email;skype
Tom;Tom Techie;123 123546;[email protected];skypenick
Mike;Mike Mechanic;000 123456;[email protected];-Mike-M-
Archie;Archie Architect;050 987654;archie@archie""".splitlines()

d = {}
h = None
for i in file: # works the same for your csv-file
    # first row == header, store in h
    if h is None:
        h = i.strip().split(";")[1:]
        continue # done for first row

    x = i.strip().split(";")

    # zip pairs the read in line with the header line to get tuples
    # wich are fed into the dict constructor that creates the inner dict
    d[x[0]] = dict(zip(h,x[1:]+[""])) # no default for skype

    # use this instead if you want the skype key always present with empty default
    # d[x[0]] = dict(zip(h,x[1:]+[""]))  

print(d)

zip() discards the elements of the longer list - you won't need any checks for that.

Output:

{'Tom':   {'name': 'Tom Techie', 'phone': '123 123546', 
           'email': '[email protected]', 'skype': 'skypenick'}, 
 'Mike':   {'name': 'Mike Mechanic', 'phone': '000 123456', 
            'email': '[email protected]', 'skype': '-Mike-M-'}, 
 'Archie': {'name': 'Archie Architect', 'phone': '050 987654', 
            'email': 'archie@archie'}}

If you use the commented line, the data will get a default value of '' for the skype - works only b/c skype is the last element of the splitted line

Comments

0

You can use a dict comprehension! Assuming the data is something like this here...

with open("df.csv", "r") as file:
  d = {x.split(";")[0]:{
      "name": x.split(";")[2], 
      "phone": x.split(";")[3],
      "email": x.split(";")[1], 
      "skype": x.split(";")[4][:-1] # Slice off trailing newline
  } for x in file}
  d.pop("")

We want to open files using with whenever possible to benefit from Python's context management. See https://www.python.org/dev/peps/pep-0343/ for fundamental understanding of the with statement.

Since the key "" only appears once at the head of the csv, we can pop it at the end and avoid performing a comparison at every iteration. A dict comprehension accomplishes the same thing you wanted to achieve with d.update.

More about comprehensions: https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions

Edit: refactoring to remove the repetitive calls to .split can look something like this:

def line_to_dict(x, d):
  x = x.split(";")
  d[x[0]] = {
      "name": x[2], 
      "phone": x[3],
      "email": x[1], 
      "skype": x[4][:-1] # Slice off trailing newline
  }

with open("df.csv", "r") as file:
  d = {}
  for x in file:
    line_to_dict(x, d)
  d.pop("")

Comments

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.