1

I have two lists of dictionaries:

dict_list1 = [{'k1':1, 'k2':2}, {'k1':3, 'k2':4}]
dict_list2 = [{'k1':1, 'k2':2, 'k3':10}, {'k1':3, 'k2':4, 'k3':10}]

And now for each dict_x in dict_list1, I want to know if there is a dict_y on dict_list2 that contains every key,value from dict_x.

I cannot think of another way of doing this other then:

for dict_x in dict_list1:
    for dict_y in dict_list2:
        count = len(dict_x)
        for key, val in dict_x.items():
            if key in dict_y and dict_y[key] == val:
                count -= 1
        if count == 0:
            print('YAY')
            break

3 Answers 3

4

dict views can perform quick "is subset" testing via the inequality operators. So:

if dict_x.items() <= dict_y.items():  # Use .viewitems() instead of .items() on Python 2.7

will only return true if every key/value pair in dict_x also appears in dict_y.

This won't change anything in terms of big-O performance, but it does make the code somewhat cleaner:

for dict_x in dict_list1:
    for dict_y in dict_list2:
        if dict_x.items() <= dict_y.items():
            print('YAY')
            break

Note that creating the views costs something (it's just a fixed cost, not dependent on dict size), so if performance matters, it may be worth caching the views; doing so for dict_list1 is free:

for dict_x in dict_list1:
    dict_x_view = dict_x.items()
    for dict_y in dict_list2:
        if dict_x_view <= dict_y.items():
            print('YAY')
            break

but some eager conversions would be needed to cache both:

# Convert all of dict_list2 to views up front; costs a little if
# not all views end up being tested (we always break before finishing)
# but usually saves some work at the cost of a tiny amount of memory
dict_list2_views = [x.items() for x in dict_list2]
for dict_x in dict_list1:
    dict_x_view = dict_x.items()
    for dict_y_view in dict_list2_views:
        if dict_x_view <= dict_y_view:
            print('YAY')
            break

You could also collapse the loop using any (which removes the need to break since any short-circuits), so the first (simplest) check could become:

for dict_x in dict_list1:
    if any(dict_x.items() <= dict_y.items() for dict_y in dict_list2):
       print('YAY')

This could be further collapsed to a single list comprehension that results in the various matches, but at that point the code is going to be pretty cramped/ugly:

for _ in (dict_x in dict_list1 if any(dict_x.items() <= dict_y.items() for dict_y in dict_list2)):
    print('YAY')

though without knowing what you'd really do (as opposed to just printing YAY) that's getting a little pointless.

Sign up to request clarification or add additional context in comments.

1 Comment

Accepting @ShadowRanger answer because it is more complete.
3

Below, I use the fact that the dict.items view implements set operations to check for each d1.items() if there exists a d2.items(), such that d1.items() is a subset of d2.items()

[any(d1.items() <= d2.items() for d2 in dict_list2) for d1 in dict_list1]

1 Comment

My current version returns a list the length of dict_list1 representing whether or not some element of dict_list2 is a superdict of the dict at that index in dict_list1.
-1

You can use any and all:

dict_list1 = [{'k1':1, 'k2':2}, {'k1':3, 'k2':4}]
dict_list2 = [{'k1':1, 'k2':2, 'k3':10}, {'k1':3, 'k2':4, 'k3':10}]
v = [any(all(c in i and i[c] == k for c, k in b.items()) for i in dict_list2)\
   for b in dict_list1]

Output:

[True, True]

4 Comments

This only checks keys, not values.
Also, doesn't it have multiple loops just put in a 1-liner?
Yes, I supose there are 3 fors as well, but in one single line
Try dict_list1 = [{'k1':1, 'k2': None}] and dict_list2 = [{'k1':1}].

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.