Baz
Baz

Reputation: 13185

Dictionary comprehension to group keys in a dictionary

Is it possible to use a dictionary comprehension to group keys in a dictionary?

d = {0: ("A",), 1: ("BB",), 2: ("BB",), 3: ("D",), 4: ("E",)}
l = [(0, 1, 2, 4), (3,)]

to produce:

d = {(0, 1, 2, 4): ("A","BB", "E"), (3,): ("D",)}

UPDATE:

I originally had the values as sets but have changed this to tuples. A tuple(set(x)) operation could be used when creating ("A","BB", "E") instead of ("A","BB","BB","E")

Upvotes: 2

Views: 321

Answers (3)

TrebledJ
TrebledJ

Reputation: 9007

This answer demonstrates a solution using simple loops and comprehensions. Thus, it may not be as effective as other answers.

These are your pre-defined variables, note that I removed one depth from l (similar to DeepSpace's answer).

d = {0: ("A",), 1: ("BB",), 2: ("BB",), 3: ("D",), 4: ("E",)}
l = [(0, 1, 2, 4), (3,)]

Using normal loops:

w_normal = {}
for i in l:
  temp = []
  for j in i:
    temp += [d[j]]
  w_normal[tuple(i)] = temp

Using a dictionary comprehension (as requested):

w_comp = {tuple(i): [d[j] for j in i] for i in l}

Both values output when printed

{(0, 1, 2, 4): [('A',), ('BB',), ('BB',), ('E',)], (3,): [('D',)]}

Rather than using tuple(set(x)) to create [('A',), ('BB',), ('E',)], preserving [('A',), ('BB',), ('BB',), ('E',)] will allow you to index ('BB',) and ('E',) with respect to the key-tuple.

However, if you're persistent you could do the tuple(set(x)) with

w_comp = {tuple(i): tuple({d[j] for j in i}) for i in l}

Upvotes: 2

DeepSpace
DeepSpace

Reputation: 81684

I'd use a defaultdict. Also note that I changed the format of l a bit, I don't think it should contain nested tuples.

d = {0: ("A",), 1: ("BB",), 2: ("BB",), 3: ("D",), 4: ("E",)}
l = [(0, 1, 2, 4), (3,)]

output = defaultdict(list)

for key_group in l:
    output[key_group].extend(d[key] for key in key_group)

print(output)
# defaultdict(<class 'list'>, {(0, 1, 2, 4): [('A',), ('BB',), ('BB',), ('E',)], (3,): [('D',)]})

The values in output are lists of tuples since tuples are immutable. The below snippet will produce an output which is closer to the output you provided by flattening the values (except for using lists as values insted of tuples for the same reason):

d = {0: ("A",), 1: ("BB",), 2: ("BB",), 3: ("D",), 4: ("E",)}
l = [(0, 1, 2, 4), (3,)]

output = defaultdict(list)

for key_group in l:
    output[key_group].extend(chain.from_iterable(set(d[key] for key in key_group)))

print(output)
# defaultdict(<class 'list'>, {(0, 1, 2, 4): ['A', 'E', 'BB'], (3,): ['D']})

For completeness, the code from OP's answer which uses defaultdict(set):

from collections import defaultdict
from itertools import chain

d = {0: ("A",), 1: ("BB",), 2: ("BB",), 3: ("D",), 4: ("E",)}
l = [(0, 1, 2, 4), (3,)]

output = defaultdict(set)

for key_group in l:
    output[key_group].update(chain.from_iterable(d[key] for key in key_group))
print(output)
# defaultdict(<class 'set'>, {(0, 1, 2, 4): {'BB', 'A', 'E'}, (3,): {'D'}})

Upvotes: 4

Baz
Baz

Reputation: 13185

A solution similar to DeepSpace's but using a defaultdict(set).

d = {0: ("A",), 1: ("BB",), 2: ("BB",), 3: ("D",), 4: ("E",)}
l = [(0, 1, 2, 4), (3,)]

output = defaultdict(set)

for key_group in l:
    output[key_group].update(d[key] for key in key_group)

Upvotes: 1

Related Questions