cyfex
cyfex

Reputation: 55

Create two dictionaries by iterating through a function that returns a tuple of two elements in Python

I want to create two dictionaries in python by dictionary comprehension at the same time. The two dictionaries share the same key set, but have different values for each key. Therefore, I use a function to return a tuple of two values, and hoping a dictionary comprehension can create these two dictionaries at the same time.

Say, I have a function

def my_func(foo):
    blablabla...
    return a, b

And I will create two dictionaries by

dict_of_a, dict_of_b = ({key:my_func(key)[0]}, {key:my_func(key)[1]} for key in list_of_keys)

Is there any better code to improve it? In my opinion, my_func(key) will be called twice in each iteration, slowing down the code. What is the correct way to do it?

Upvotes: 1

Views: 300

Answers (5)

jferard
jferard

Reputation: 8180

The regular loop is probably the best way to go. If you want to play with functools, you can write:

>>> def func(foo): return foo[0], foo[1:]
... 
>>> L = ['a', 'ab', 'abc']
>>> functools.reduce(lambda acc, x: tuple({**d, x: v} for d, v in zip(acc, func(x))), L, ({}, {}))
({'a': 'a', 'ab': 'a', 'abc': 'a'}, {'a': '', 'ab': 'b', 'abc': 'bc'})

The function reduce is a fold: it takes the current accumulator (here the dicts being built) and the next value from L:

  • d, v in zip(acc, func(x)) extracts the dicts one at a time and the matching element of the return value of func;
  • {**d, x: v} update the dict with the current value.

I don't recommend this kind of code since it's hard to maintain.

Upvotes: 0

Prudhvi
Prudhvi

Reputation: 1115

for key in list_of_keys:
   dict_of_a[key],dict_of_b[key] = my_func(key)

Upvotes: 1

RomanPerekhrest
RomanPerekhrest

Reputation: 92854

With ordered slicing:

def myfunc(k):
    return k + '0', k + '1'

list_of_keys = ['a', 'b', 'c']

groups = [(k,v) for k in list_of_keys for v in myfunc(k)]
dict_of_a, dict_of_b = dict(groups[::2]), dict(groups[1::2])

print(dict_of_a)   # {'a': 'a0', 'b': 'b0', 'c': 'c0'}
print(dict_of_b)   # {'a': 'a1', 'b': 'b1', 'c': 'c1'}

Upvotes: 1

Richard Neumann
Richard Neumann

Reputation: 3361

You cannot create two dicts in one dict comprehension. If your primary goal is to just call my_func once to create both dicts, use a function for that:

def mkdicts(keys):

    dict_of_a = {}
    dict_of_b = {}

    for key in keys:
        dict_of_a[key], dict_of_b[key] = my_func(key)

    return dict_of_a, dict_of_b

Upvotes: 0

abdusco
abdusco

Reputation: 11081

my_func(key) will be called twice in each iteration, slowing down the code

Dont worry about it. Unless you need to do thousands/millions of iterations and the script takes an unreasonably long time to complete, you shouldn't concern with negligible optimization gains.

That said, I'd use something like this:

if __name__ == '__main__':
    def my_func(k):
        return f'a{k}', f'b{k}'


    keys = ['x', 'y', 'z']

    results = (my_func(k) for k in keys)
    grouped_values = zip(*results)
    da, db = [dict(zip(keys, v)) for v in grouped_values]

    print(da)
    print(db)

# Output:
# {'x': 'ax', 'y': 'ay', 'z': 'az'}
# {'x': 'bx', 'y': 'by', 'z': 'bz'}

Upvotes: 0

Related Questions