23

I have 5 dictionaries and I want a union of their keys.

alldict =  [dict1, dict2, dict3, dict4, dict5]

I tried

allkey = reduce(lambda x, y: set(x.keys()).union(y.keys()), alldict)

but it gave me an error

AttributeError: 'set' object has no attribute 'keys'

Am I doing it wrong ? I using normal forloop but I wonder why the above code didn't work.

joaquin
  • 82,968
  • 29
  • 138
  • 152
Tg.
  • 5,608
  • 7
  • 39
  • 52

6 Answers6

71

I think @chuck already answered the question why it doesn't work, but a simpler way to do this would be to remember that the union method can take multiple arguments:

allkey = set().union(*alldict)

does what you want without any loops or lambdas.

Duncan
  • 92,073
  • 11
  • 122
  • 156
10

Your solution works for the first two elements in the list, but then dict1 and dict2 got reduced into a set and that set is put into your lambda as the x. So now x does not have the method keys() anymore.

The solution is to make x be a set from the very beginning by initializing the reduction with an empty set (which happens to be the neutral element of the union).

Try it with an initializer:

allkey = reduce(lambda x, y: x.union(y.keys()), alldict, set())

An alternative without any lambdas would be:

allkey = reduce(set.union, map(set, map(dict.keys, alldict)))
chuck
  • 493
  • 3
  • 9
5

A simple strategy for non-functional neurons (pun intended):

allkey = []

for dictio in alldict:
    for key in dictio:
        allkey.append(key)

allkey = set(allkey)

We can convert this code to a much sorter form using set comprehensions:

allkey = {key for dictio in alldict for key in dictio}

This one-liner is still very readable in comparison with the conventional for loop. The key to convert a nested loop to a list or set comprehension is to write the inner loop (the one that varies faster in the nested loop) as the last index (that is, for key in dictio).

joaquin
  • 82,968
  • 29
  • 138
  • 152
  • @BjörnPollex You are right, the (set) comprehension successfully competes with functional alternatives both in readability and conciseness. – joaquin Jan 23 '14 at 11:01
2
set().union(dict1.keys(),dict2.keys()...)

I tried the list and it didnt work so just putting it up here for anyone.

Leon
  • 5,701
  • 3
  • 38
  • 38
1

Just one more way, 'cause what the hay:

a={}; [ a.update(b) for b in alldict ] and a.keys()

or the slightly-more-mysterious

reduce(lambda a, b: a.update(b) or a, alldict, {}).keys()

(I'm bummed that there's no built-in function equivalent to

def f(a,b):
   r = {}
   r.update(a)
   r.update(b)
   return r

is there?)

Michael Lorton
  • 43,060
  • 26
  • 103
  • 144
1

If you only want to union keys of 2 dicts you could use operator |.

Quote from docs:

Return a new set with elements from the set and all others.

Example:

all_keys = (dict1.keys() | dict2.keys())
Alex Kosh
  • 2,206
  • 2
  • 19
  • 18