Puff
Puff

Reputation: 515

Unpacking list into strings, splitting them and creating a dictionary from there

I'm using python 3.x and I want to create a dictionary from a list. That is, I have each key and value concatenated in a string, and each entry as an element in a list.

my_list = ['val1 key1', 'val2 key2', 'val2 key2']

I can split it into two lists using

values,keys = zip(*(s.split() for s in my_list))

Creating a dictionary from there is easy. Since I still need to do stuff to the keys and values, I do:

my_dict = {k[:-1]:float(v) for k,v in zip(keys,values)}

Out of mere curiosity, I was wondering If there is a way where to avoid the intermediate lists. In short I need to access each list element, split the string, do something to each split part and input it as key:value pair into a dictionary. Following this question I tried

my_dict = {k[:-1]:float(v) for v,k in zip(*(s.split() for s in my_list))}

But I get ValueError: too many values to unpack (expected 2). Then I tried simply using a generator (I think it's a generator) inside the dictionary comprehension syntax and it work. But I don't like it, since the second for is used only to extract an element from the list:

my_dict = {s[1][:-1]:float(s[0]) for s in (s.split(', ') for s in my_list)}

This is what I'm currently using and works perfectly, but I'd like to know why the second solution doesn't work. to me, it seems it should, and that my solution uses one to many for. I'm aware it's not a super relevant question, but I'd like to learn. Also, I'm open to title suggestions.

EDIT1: Fixed a few syntax errors I had.

EDIT2: A full working and explicit example with expected result, as suggested. I'm still working on making good mcve's:

my_list = ['1.123, name1\n', '2.3234, name2\n', '3.983, name3\n', '4.23, name4\n']

The output I want is what I would get if I manually did

my_dict = {'name1':1.123, 'name2':2.3234, 'name3':3.983, 'name4':4.23}

Method that creates intermediate lists:

values,keys = zip(*(s.split(', ') for s in my_list))
print(values)
>>> ('1.123', '2.3234', '3.983', '4.23')
print(keys)
>>> ('name1\n', 'name2\n', 'name3\n', 'name4\n')
my_dict = {k[:-1]:float(v) for k,v in zip(keys,values)}
print(my_dict)
>>> {'name4': 4.23, 'name2': 2.3234, 'name1': 1.123, 'name3': 3.983}

Example that I don't know why it does not work:

my_dict = {k[:-1]:float(v) for v,k in zip(*(s.split(', ') for s in my_list))}
>>> ValueError: too many values to unpack (expected 2)

Working example that to me, seems it uses one to for inside the list/dict comprehension/generator expression:

my_dict = {s[1][:-1]:float(s[0]) for s in (s.split(', ') for s in my_list)}
print(my_dict)
>>> {'name4': 4.23, 'name2': 2.3234, 'name1': 1.123, 'name3': 3.983}

My actual strings look something like '0.9493432915614861, zf_AB012_bn_BOS\n' , that's why I use a more readable example.

EDIT3: I just learned of the str.strip() method. This makes the line creating the dictionary a bit nicer:

my_dict = {s[1].strip():float(s[0]) for s in (s.split(', ') for s in my_list)}

Upvotes: 1

Views: 501

Answers (1)

bipll
bipll

Reputation: 11940

def dict_unzip(lst):
    for x in lst:
        yield reversed(x.split(' ', 1))

my_dict = dict(dict_unzip(my_list))

But since it's Python3 things are notably simpler actually:

my_dict = dict(map(lambda s: reversed(s.split(' ', 1)), my_list))

Or even

my_dict = dict(reversed(s.split(' ', 1)) for s in my_list)

Upvotes: 1

Related Questions