3

I have a list of tuples, like so:

lst_of_tpls = [(1, 'test2', 3, 4),(11, 'test12', 13, 14),(21, 'test22', 23,24)]

And I would like to convert it to a dictionary so that it looks like this:

mykeys = ['ones', 'text', 'threes', 'fours']
mydict = {'ones': [1,11,21], 'text':['test2','test12','test22'], 
          'threes': [3,13,23], 'fours':[4,14,24]}

I have tried to enumerate the lst_of_tplslike so:

mydict = dict.fromkeys(mykeys, [])
for count, (ones, text, threes, fours) in enumerate(lst_of_tpls):
    mydict['ones'].append(ones)

but this puts the values I would like to see in 'ones' also in the other "categories":

{'ones': [1, 11, 21], 'text': [1, 11, 21], 'threes': [1, 11, 21], 'fours': [1, 11, 21]}

Also, I would like to keep mykeys flexible.

Torxed
  • 22,866
  • 14
  • 82
  • 131
evilolive
  • 407
  • 1
  • 5
  • 12

2 Answers2

6

You can apply zip twice to find the proper pairings:

lst_of_tpls = [(1, 'test2', 3, 4),(11, 'test12', 13, 14),(21, 'test22', 23,24)]
mykeys = ['ones', 'text', 'threes', 'fours']
new_d = {a:list(b) for a, b in zip(mykeys, zip(*lst_of_tpls))}

Output:

{
 'ones': [1, 11, 21],
 'text': ['test2', 'test12', 'test22'],
 'threes': [3, 13, 23],
 'fours': [4, 14, 24]
}
Ajax1234
  • 69,937
  • 8
  • 61
  • 102
  • Ok, cool that works. A lot of new syntax for me; let's see if I understand it: `zip(*lst_of_tpls)` returns an iterator with the first, second, ... element of the tuples. The outer zip adds the keys to that. So now I have an iterator for key, tuple pairs. Then with this iterator I generate the dictionary and on the fly I convert the tuples the first zip would give me to a list. – evilolive Nov 15 '18 at 09:40
  • @evilolive That is correct. `zip(*lst_of_tpls)` pairs the elements in `lst_of_tpls`, and the second `zip` pairs each element in `mykeys` with each element in `zip(*lst_of_tpls)`. – Ajax1234 Nov 15 '18 at 15:36
1

You can pass to dict tuples of (key, value), it's twice faster than use dictionary comprehension

lst_of_tpls = [(1, "test2", 3, 4), (11, "test12", 13, 14), (21, "test22", 23, 24)]
mykeys = ["ones", "text", "threes", "fours"]
my_dict = dict(zip(mykeys, zip(*lst_of_tpls)))

Output:

{'ones': (1, 11, 21),
 'text': ('test2', 'test12', 'test22'),
 'threes': (3, 13, 23),
 'fours': (4, 14, 24)}

Profiler example:

lst_of_tpls = [(1, "test2", 3, 4), (11, "test12", 13, 14), (21, "test22", 23, 24)]
mykeys = ["ones", "text", "threes", "fours"]


def dict_comprehension():
    return {a: list(b) for a, b in zip(mykeys, zip(*lst_of_tpls))}


def dict_generator():
    return dict(zip(mykeys, zip(*lst_of_tpls)))


if __name__ == "__main__":
    import timeit

    funcs = (dict_comprehension, dict_generator)
    for f in funcs:
        result = timeit.timeit(f, number=10000, globals=globals())
        print(f"{f.__name__}: {result:.5f}")


dict_comprehension: 0.05009 
dict_generator: 0.02468
Vlad Bezden
  • 83,883
  • 25
  • 248
  • 179
  • Interesting. Can you try to explain what the difference between the dict-comprehension and the creation of a new dict(...) is? Is this a general rule that also applies to [...] vs. list(...) creation? – evilolive Jun 28 '19 at 08:28