0

I have a CSV file I'd like to parse as a Dict, so that the first item with the key of "key" can be unpacked to a variable, then the rest of the values as a list of keyed languages. Is there a nice, clean way of doing this?

My CSV is like so:

key,english,spanish
wanna_play_a_game,"Wanna play a game?","¿Quiero jugar a un juego?"
something_else,"Hello!","hola!"

Dict object as such:

[{'key': 'wanna_play_a_game', 'english': 'Wanna play a game?', 'spanish': '¿Quiero jugar a un juego?'}, {'key': 'something_else', 'english': 'Hello', 'spanish': 'hola!'}]

I did have a go but then realised that unpacking isn't the same with dicts (I assume) because they're not ordered like with lists. Am I overcomplicating things and should just saved the row['key'] to a variable and then remove it from the dict? Python isn't my bread & butter language so I'm not that knowledgable on the more magicky bits of the syntax.

This is what I was kinda getting at before realising I'm lost:

import csv
class Translate:

    def __init__(self):
        self.translations = self.readCSV('translations.csv')

    def readCSV(self, filename):
        with open(filename, 'rt') as csvfile:
            reader = csv.DictReader(csvfile)
            return [dict(d) for d in reader]

    def parse(self, line):
        # for t_key, *langs in self.translations:
        # for k, (t_key, *langs) in self.translations:
        # ... some magic function?

The basic thing I'm trying to get is t_key to be "wanna_play_a_game" and langs to be ('english': 'Wanna play a game?', 'spanish': '¿Quiero jugar a un juego?')

Matt Fletcher
  • 8,182
  • 8
  • 41
  • 60
  • What exactly is your code doing wrong? – glibdud Dec 07 '17 at 16:44
  • Your question is tagged Python 3.6. In 3.6, [`DictReader`](https://docs.python.org/3/library/csv.html#csv.DictReader) should actually return `OrderedDict` instances, so unpacking should not be a problem if you just change `return [dict(d) for d in reader]` to `return list(reader)`. – tobias_k Dec 07 '17 at 16:47
  • Ok so you're trying to build a nested dictionary? Something like `[{'t_key':'wanna_play_a_game','langs':{'english':'Wanna play a game?','spanish':'¿Quiero jugar a un juego?'},...]` – MCBama Dec 07 '17 at 16:47
  • @glibdud I'm asking if there's a way of doing argument unpacking-like shorthand syntax, like you might with a list, but with a dict. I don't think the question is off-topic, broad or unclear (personally at least) – Matt Fletcher Dec 07 '17 at 16:48
  • The nested dictionary might be nice, either really. I come from JS/Node mainly and am mildly baffled by things like `OrderedDict`, which I've never seen before. Also seemed that `list()` was doing something different to people using py2 and py3.6 – Matt Fletcher Dec 07 '17 at 16:50
  • Ok, so by "unpacking" do you want to have two dictionaries at the end? because if so [this](https://stackoverflow.com/questions/6612769/is-there-a-more-elegant-way-for-unpacking-keys-and-values-of-a-dictionary-into-t) is what you want. – MCBama Dec 07 '17 at 16:51
  • Actually, maybe I wasn't clear in my question. The reason is because there's one key, but possibly hundreds of languages. So I wanted to say "choose the key, then stick everything else in the langs", like unpacking might do with an unknown-length array. I also want to not have to specify each language itself by name. But really that may just be hammering a nail with a sausage. Just wanted something short and elegant – Matt Fletcher Dec 07 '17 at 16:55

3 Answers3

1

Unpacking is not what you want here. You should just pop t_key from the dict and then iterate over the rest.

t_key = langs.pop('key')
for key, value in langs.items():
    ...
Daniel Roseman
  • 588,541
  • 66
  • 880
  • 895
  • Ah, I was trying to do something like that (as a non-shorthandy way), but I think perhaps my dict was still being passed as reference? Or something to do with popping it made it break on the next loop. It seems that it was being mutated, but could also possibly be a dud row with an empty key – Matt Fletcher Dec 07 '17 at 16:52
1

Your question is tagged Python 3.6, and in 3.6, DictReader returns OrderedDict instances, so unpacking should actually not be a problem, if you just change return [dict(d) for d in reader] to return list(reader), otherwise you are converting those OrderedDict back to regular unordered dict.

Tested with the online interactive Python shell as I'm stuck with 3.5 here:

>>> text = '''key,english,spanish
... wanna_play_a_game,"Wanna play a game?","¿Quiero jugar a un juego?"
... something_else,"Hello!","hola!"'''
>>> r = csv.DictReader(io.StringIO(text))
>>> ds = list(r)
>>> key, eng, esp = ds[0].values()
>>> key, eng, esp
('wanna_play_a_game', 'Wanna play a game?', '¿Quiero jugar a un juego?')

Or if the number of languages is not known, * unpack to a list:

>>> key, *langs = ds[1].values()
>>> key, langs
('something_else', ['Hello!', 'hola!'])

In your case, you probably need this (also remember to return list(reader)):

for d in self.translations:
    key, *langs = d.values()
    # do stuff with key and langs

Alternatively, you could also use a dict-comprehension to create a dictionary mapping the value of the "key" key to an OrderedDict of the translations for the different languages:

>>> {d.pop("key"): d for d in ds}
{'wanna_play_a_game': OrderedDict([('english', 'Wanna play a game?'), ('spanish', '¿Quiero jugar a un juego?')]),
 'something_else': OrderedDict([('english', 'Hello!'), ('spanish', 'hola!')])}

Note, however, that this modifies the dicts in the list as you create the dictionary comprehension, which can be considered bad practice, but in this case, and considering that you do not keep a reference to the original dicts in the first place, I think this can be used.

tobias_k
  • 81,265
  • 12
  • 120
  • 179
  • Thank you so much, I think this is putting me on the right track. However, I'm getting `'list' object has no attribute 'values'` when using `for key, *langs in self.translations.values():` – Matt Fletcher Dec 07 '17 at 17:06
  • I changed the `readCSV` method to use `return list(reader)` as you mentioned above ^^^ – Matt Fletcher Dec 07 '17 at 17:09
  • @MattFletcher Note that you are doing `self.translations.values()`, whereas my code would correspond to `self.translations[0].values()`, i.e. get the `values` from the dicts _inside_ the list, not the list itself. Of course, in your case you should probably use a loop like `for d in self.translations: key, *lang = d.values()` – tobias_k Dec 07 '17 at 17:10
  • YES! That's the one. Ah, I feel stupid now that I was trying to run a dict function on a list... But yes, the code works and is exactly what I asked for, super clean and lovely. Thanks a bunch. I think I get those chimera `OrderedDict`-s a bit more now – Matt Fletcher Dec 07 '17 at 17:14
-3

You can try something like this using the split() method :

final_list=[]
with open('file.txt','r') as f:


    for line_no,data in enumerate(f):
        if line_no==0:
            pass
        else:
            dict_1 = {}

            key,english,spanish=data.split(',')
            dict_1['key'] = key
            dict_1['English'] = english


            dict_1['Spanish']=spanish.strip('\n')

            final_list.append(dict_1)

print(final_list)

output:

[{'English': '"Wanna play a game?"', 'key': 'wanna_play_a_game', 'Spanish': '"¿Quiero jugar a un juego?"'}, {'English': '"Hello!"', 'key': 'something_else', 'Spanish': '"hola!"'}]
Aaditya Ura
  • 12,007
  • 7
  • 50
  • 88
  • 1
    How does this solve OPs problem? You are just replacing the tested and proven `csv.DictReader` with your own implementation. – tobias_k Dec 07 '17 at 17:13
  • Also in the comments on the question, we go through a lot of the requirements, such as not specifying the keys of the languages, and having something short and elegant. – Matt Fletcher Dec 07 '17 at 17:15
  • @tobias_k if i don't want to use csv and created my own logic , is it wrong ? its giving same result as OP shown as expected output. – Aaditya Ura Dec 07 '17 at 17:15