0

I have a couple lines to populate a set.

x = {1: {2: 4, 3: 6}, 5: {2:6, 10: 25, 14: 12}}

keys = set()
for y in x:
    for z in x[y]:
        keys.add(z)

# keys is now `set([2, 3, 10, 14])`

I can't shake the feeling that I can do this better, but nothing I come up with is seems great. Most implementations build a list first, which is annoying. There are lots of x in y, and most y have the same z.

# Builds a huuuuge list for large dicts.
# Adapted from https://stackoverflow.com/a/953097/241211
keys = set(itertools.chain(*x.values()))

# Still builds that big list, and hard to read as well.
# I wrote this one on my own, but it's pretty terrible.
keys = set(sum([x[y].keys() for y in x], []))

# Is this what I want?
# Finally got the terms in order from https://stackoverflow.com/a/952952/241211
keys = {z for y in x for z in x[y]}

Is the original code the "most pythonic" or is one of the one-liners better? Something else?

Michael
  • 8,362
  • 6
  • 61
  • 88
  • I would say that approaches 1 (loop), 2 (set from chain which does **not** build a list), and 4 (nested set comprehension) are all valid, Pythonic and algorithmically sound. 3 (sum of lists) has quadratic time complexity and should be discarded. Which of the 3 you chose is a matter of taste and whether you like it readable, concise or C-optimized. – user2390182 Jan 12 '18 at 21:59

3 Answers3

6

I would use

{k for d in x.itervalues() for k in d}

itervalues() (just values() in Python 3) doesn't build a list, and this solution doesn't involve dictionary lookup (in contrast to {z for y in x for z in x[y]}).

vaultah
  • 44,105
  • 12
  • 114
  • 143
4

I would use the itertools module, specifically the chain class.

>>> x = {1: {2: 4, 3: 6}, 5: {2:6, 10: 25, 14: 12}}
>>> from itertools import chain
>>> set(chain.from_iterable(x.itervalues()))
set([2, 3, 10, 14])
chepner
  • 497,756
  • 71
  • 530
  • 681
-1

You can use dict.items():

x = {1: {2: 4, 3: 6}, 5: {2:6, 10: 25, 14: 12}}
final_x = set(i for b in [b.keys() for i, b in x.items()] for i in b)

Output:

set([2, 3, 10, 14])
Ajax1234
  • 69,937
  • 8
  • 61
  • 102
  • 2
    `dict.items()` in Python 2 creates a list of dictionary items, which is something the OP wants to avoid. Then `[b.keys() for i, b in x.items()]` creates another list. In that list comprehension you call `dict.keys()`, which creates yet another list. – vaultah Jan 12 '18 at 21:57