4

I have a list of sorted integer IDs like

[1, 2, 10, 15, 16, 17, 20, 34, ...]

I have a tuple (tuple1) of tuples of codes next to an ID sorted by ID like

((1, "A"), (2, "A"), (15, "B"), (16, "A"), (17, "B"), (34, "B"), ...)

I have another tuple (tuple2) of tuples in the same format like

((1, "B"), (2, "B"), (10, "B"), (16, "A"), (17, "B"), (34, "B"), ...)

I want to combine the tuples into a dictionary whereby the key is the ID and the value is a list containing their code from tuple1 and their code from tuple2 in that order. If the ID exists in the ID list but not in the tuple then the value should be "N/A".

Therefore, using the above data the following should be produced:

{1: ["A", "B"], 2: ["A", "B"], 10: ["N/A", "B"], 15: ["B", "N/A"],
 16: ["A", "A"], 17: ["B", "B"], 20: ["N/A", "N/A"], 34: ["B", "B"]}

I have spent quite a while thinking about this problem but I cannot come up with a solution. If someone could help me figure out how to get it working in Python then that would be extremely helpful.

Thanks.

EDIT: It is not a duplicate, this problem is considerably more complex.

busybear
  • 10,194
  • 1
  • 25
  • 42
Jack P
  • 469
  • 1
  • 6
  • 16

5 Answers5

10

If you make your tuple of tuples into a dictionary, it will be a lot easier. Use get to set a default value for your dictionary if the key is absent:

ids = [1, 2, 10, 15, 16, 17, 20, 34]
tup1 = ((1, "A"), (2, "A"), (15, "B"), (16, "A"), (17, "B"), (34, "B"))
tup2 = ((1, "B"), (2, "B"), (10, "B"), (16, "A"), (17, "B"), (34, "B"))

tup1 = dict(tup1)
tup2 = dict(tup2)    
{k: [tup1.get(k, 'N/A'), tup2.get(k, 'N/A')] for k in ids}
busybear
  • 10,194
  • 1
  • 25
  • 42
  • I assume you meant `tup1 = (1, "A"), (2, "A"), (15, "B"), (16, "A"), (17, "B"), (34, "B")`, `tup1 = dict((x, y) for x, y in tup1)`. However, I still get the error "AttributeError: 'dict' object has no attribute 'getitem'". – Jack P Mar 29 '19 at 18:28
  • @JackP I mistakenly used `getitem` when I first posted. It should be `get`. – busybear Mar 29 '19 at 18:28
  • And I did mean to create a dictionary from the tuples. It allows you to use `get` and assign a default value: `'N/A'`. – busybear Mar 29 '19 at 18:29
  • It works without the extra list comprehension dict expression now you've added the extra parenthesis. Thanks. – Jack P Mar 29 '19 at 18:30
  • Good to hear. I clarified my intention in the code regardless. – busybear Mar 29 '19 at 18:32
3

Here is one possible solution to this:

from collections import defaultdict

keys = [1, 2, 10, 15, 16, 17, 20, 34]

t1 = ((1, "A"), (2, "A"), (15, "B"), (16, "A"), (17, "B"), (34, "B"))
t2 = ((1, "B"), (2, "B"), (10, "B"), (16, "A"), (17, "B"), (34, "B"))

merge_d = defaultdict(list)
for d in map(dict, (t1, t2)):
    for k in keys:
        merge_d[k].append(d.get(k, "N/A"))

The resulting dict will contain:

>>> merge_d
defaultdict(<class 'list'>, {1: ['A', 'B'], 2: ['A', 'B'], 10: ['N/A', 'B'], 15: ['B', 'N/A'], 16: ['A', 'A'], 17: ['B', 'B'], 20: ['N/A', 'N/A'], 34: ['B', 'B']})
cglacet
  • 8,873
  • 4
  • 45
  • 60
0

It's a little gross, but you can do it without converting to a dictionary:

myList = [1, 2, 10, 15, 16, 17, 20, 34]
tuples1 = ((1, "A"), (2, "A"), (15, "B"), (16, "A"), (17, "B"), (34, "B"))
tuples2 = ((1, "B"), (2, "B"), (10, "B"), (16, "A"), (17, "B"), (34, "B"))

myDict = {} 

for i in myList:
    myDict[i] = ["N/A","N/A"] 

for t in tuples1: 
    if(t[0]) in myList:
        for key in myDict:
            if key == t[0]:
                myDict[key][0] = t[1]

for t in tuples2: 
    if(t[0]) in myList:
        for key in myDict:
            if key == t[0]:
                myDict[key][1] = t[1]

print(myDict)

Gives:

{1: ['A', 'B'], 2: ['A', 'B'], 10: ['N/A', 'B'], 15: ['B', 'N/A'], 16: ['A', 'A'], 17: ['B', 'B'], 20: ['N/A', 'N/A'], 34: ['B', 'B']}

Salvatore
  • 10,815
  • 4
  • 31
  • 69
  • Thanks. Sorry if I wasn't clear but the ID should only be present in the final dictionary if it is present in the given list of IDs. – Jack P Mar 29 '19 at 18:41
  • @JackP No problem. I'll update the answer, you just use the elements in the list instead of a generator. – Salvatore Mar 29 '19 at 18:47
  • Ah yes that seems to have worked thanks. Although I think it is a bit slower than busybear's solution. – Jack P Mar 29 '19 at 18:48
0

This can be done another way!

lis=[1, 2, 10, 15, 16, 17, 20, 34]
tuple1=((1, "A"), (2, "A"), (15, "B"), (16, "A"), (17, "B"), (34, "B"))
tuple2=((1, "B"), (2, "B"), (10, "B"), (16, "A"), (17, "B"), (34, "B"))

z={**dict.fromkeys(lis,'N/A'), **dict(tuple1)}
y={**dict.fromkeys(lis, 'N/A'), **dict(tuple2)}    


ds = [z, y]
d = {}
for k in y.keys():
    d[k] = list(d[k] for d in ds)
Kartikeya Sharma
  • 1,335
  • 1
  • 10
  • 22
0

My two cents: a one-line solution, based on @busybear's answer, able to handle an arbitrary number of tuples:

ids = [1, 2, 10, 15, 16, 17, 20, 34]
vals = (
    ((1, "A"), (2, "A"), (15, "B"), (16, "A"), (17, "B"), (34, "B"),),
    ((1, "B"), (2, "B"), (10, "B"), (16, "A"), (17, "B"), (34, "B"),),
)

d = {i: [d.get(i, 'N/A') for d in map(dict, vals)] for i in ids}
PieCot
  • 3,564
  • 1
  • 12
  • 20