Match dictionary keys and values in new dictionary

Viewed 136

I trying to match keys and values in a dictionary together and put them into a new dictionary.

My initial dictionary looks like this:

d = {1: [5,6], 3: [4], 8: [2,3]}

I know that I can access keys and values using d.keys() and d.values().

My goal is to find all items which relate. I think it is best explained if I illustrate my desired output.

I wish to create a new dict that gives me:

finaldict = {1: [5,6], 2: [3,4,8], 3: [2,4,8], 4:[2,3,8] 5:[1,6], 6:[1,5], 8:[2,3,4]}

That is, I want to get keys of all numbers, and give values to what number they are related to.

My attempt:

d = {1: [5,6], 3:[4],8:[2,3]}
print(d)
keys = list(d.keys())
vals = list(d.values())
for i in range(0,len(d.keys())):
    current_vals = list(vals[i])
    length = len(current_vals)
    for v in current_vals:
        if v in d.keys(): #if the dictionary exists, then append
            add = [keys[i],current_vals]
            d[v].append(add)
        else:
            add2 = [keys[i],current_vals]
            empty = []
            for a in add2:
                if type(a) == int:
                    empty.append(a)
                if type(a) == list:
                    for b in a:
                        empty.append(b)
            d[v] = empty

keys = list(d.keys())
vals = list(d.values())
for i in range(0,len(keys)):
    if keys[i] in vals[i]:
        vals[i].remove(keys[i])

finaldict = {}
for j in range(0,len(keys)):
    finaldict[keys[j]] = vals[j]

print("Final dict:\n",finaldict)

My attempt gives me the output

Final dict:

 {1: [5, 6], 3: [4, [8, [2, 3]]], 8: [2, 3], 5: [1, 6], 6: [1, 5], 4: [3], 2: [8, 3]}

As you can see, finaldict[3] has the values [4, [8, [2, 3,]]]. The values themselves are wrong (3 should not be there), and also I want this to be a single list, and not on the format it is now. There are also other issues, such as finaldict[2] having the values [8, 3] when it should, in fact, have the values [3, 4, 8] and finaldict[4] only having [3], when it should have [2, 3, 8].

3 Answers

Not the most beautiful code I've ever written, but here it goes.

I create sets with all the numbers that are related to each other, then create a dictionary of each of those numbers as key, with the other ones as values.

d = {1: [5,6], 3: [4], 8: [2,3]}

pools = []
for key,values in d.items():
    for pool in pools:
        if key in pool or any(val in pool for val in values):
            pool.add(key)
            [pool.add(val) for val in values]
            break
    else:
        pools.append(set((key, *values)))
#pools = [{1, 5, 6}, {8, 2, 3, 4}]
finaldict = {k:set.difference(v, set((k,))) for pool in pools for k, v in zip(pool, [pool]*len(pool))}
print(finaldict)

You can build up the resulting dictionary by merging related groups and assigning the same merged group to every key that is part of it:

d = {1: [5,6], 3: [4], 8: [2,3]}

related = dict()
for key,group in d.items(): # merge/assign groups to keys
    merged = set(group).union({key},*(related.get(k,[]) for k in group))
    related.update((k,merged) for k in merged)
related = {k:list(g-{k}) for k,g in related.items()} # exclude key from group

print(related)
{1: [5, 6], 5: [1, 6], 6: [1, 5], 3: [8, 2, 4], 4: [8, 2, 3], 
 2: [8, 3, 4], 8: [2, 3, 4]}

You can use recursion:

d = {1: [5,6], 3: [4], 8: [2,3]}
def groups(v, seen = [], c = []):
   if (k:=[i for a, b in d.items() for i in ({a, *b} if v in {a, *b} else []) if i not in {*seen, v}]):
      for i in k:
         yield from groups(i, seen=seen+[v], c = c+([v] if seen else []))
   else:
       yield c+[v]
       
r = {i:[*{j for k in groups(i) for j in k}] for a, b in d.items() for i in [a, *b]}

Output:

{1: [5, 6], 5: [1, 6], 6: [1, 5], 3: [8, 2, 4], 4: [8, 2, 3], 8: [2, 3, 4], 2: [8, 3, 4]}
Related