How to intersect dictionaries? [duplicate]
Asked Answered
E

2

1

I haven't taken python in a long time and am a bit rusty but for the first question I'm taking a dictionary and need to intersect it returning the key and the value. So for example, I'm entering

a = {1:'a1', 2.5:'a2', 4:'a3'}
b = {1:'a1', 3:'a2',  5:'a4'}

If I enter c = intersect(a,b), I want it to return {1:'a1'}, but I only get back {'a1'}.

My code so far is:

def intersect(a, b):
    for i in a:
        if j in b:
            if a[i]==b[i]:
                return ({i})
        else:
            return {}
Ecto answered 14/1, 2018 at 2:9 Comment(5)
Sorry I meant {1:'a1'} (laptop is autocorrecting) which is in both a and b.Ecto
So {k: v for (k, v) in a.items() if k in b}?Kristelkristen
What if values don't match?Terraqueous
Your intersect code tests j but never assigns it anything.Dearr
The way in which you're iterating over each of the dictionaries only iterates over the keys, not the values. To access the values, use either dict.items() or dict.values().Copro
O
6

You could simplify this, using actual set intersection. This retains only shared items (key and value the same):

def intersect(a, b):
    return dict(a.items() & b.items())

If your dict params have non-hashable values, you can go for a dict comprehension along the following lines:

def intersect(a, b):
    return {k: a[k] for k in a if b.get(k, not a[k]) == a[k]}

And you can fix your original approach like so:

def intersect(a, b):
    d = {}
    for i in a:
        if i in b and a[i] == b[i]:
            d[i] = a[i]
    return d
Olinger answered 14/1, 2018 at 2:26 Comment(8)
Could you tell me why you used items and dict? In my first code I wrote the same things except I didn't have those two included.Ecto
@coco_11 set(a) is equivalent to set(x for x in a) and if a is a dict, this will only produce its keys. Hence the items call. And since items are a list of pairs, you have to call dict on it again.Olinger
In Python 3, items aren't given as a list. Try dict(a.items() & b.items()).Boride
@StefanPochmann True, but the solution given in my answer works in both Py 2 and 3. And the question is not tagged with a specific version.Olinger
Yeah I just meant your comment "items are a list of pairs"Boride
@StefanPochmann Yeah, the point there was on the pairs. Whatever the type of iterable, if it produces pairs, you can pass it to the dict constructor.Olinger
FWIW, I think the if b.get(k, not a[k]) == a[k] is ingenious.Chimaera
From 2021: Python 2 is thankfully long dead, so dict(a.items() & b.items()) is the correct approach. Coercing two ItemsView containers into sets needlessly consumes space and time.Ghent
T
5

Here is a version of the intersect function, that intersects the keys and uses the values of the first dictionary, that means in case of different values, this function is biased towards the value in the first dictionary:

def intersect(a, b):
    return {k: a[k] for k in a.keys() & b.keys()}

This works because, the dict_keys view objects returned by dict.keys() (since Python 3) are sets (i.e. implement collections.abc.Set).

If you want to customize, which value will be used, we can use a function, that takes both values:

def intersect(a, b, select=lambda a, b: a):
    return {k: select(a[k], b[k]) for k in a.keys() & b.keys()}

Instead of returning one of the values, we could then also e.g. add the values:

>>> intersect({'a': 4, 'b': 3}, {'a': 5}, lambda a, b: a + b)
{'a': 9}
Terraqueous answered 14/1, 2018 at 2:31 Comment(2)
But this does not enforce k to have the same value in a and bOlinger
Nothing in the OPs question stated, that this is desired. I also provided a way to customize the behaviour.Terraqueous

© 2022 - 2024 — McMap. All rights reserved.