Martin
Martin

Reputation: 1177

How to compare dictionaries when the case of the value does not matter?

I have two dictionaries d1 and d2:

>>> d1 = {'name':'John Smith', 'age':36}
>>> d2 = {'name':'john smith', 'age':36}

I want to make sure, that two dictionaries are identical, but I don't care about the name value case. In addition, I need to avoid modifying the dictionaries.

I could solve it by copying directories like this:

>>> d1_lc = d1.copy()
>>> d2_lc = d2.copy()
>>>
>>> d1_lc['name'] = d1['name'].casefold()
>>> d2_lc['name'] = d2['name'].casefold()
>>>
>>> if d1_lc == d2_lc:
...     do_something_with_d1(d1)
...

Perhaps there is a better way?

Upvotes: 0

Views: 101

Answers (3)

d.b
d.b

Reputation: 32548

Use a generator expression to check each value:

all(d1[k] == d2[k] if k != "name" else d1[k].lower() == d2[k].lower() for k in d1)
# True

You can also add set(d1.keys()) == set(d2.keys()) if necessary.

Upvotes: 3

Wizard.Ritvik
Wizard.Ritvik

Reputation: 11612

Here's a solution using dataclasses which I personally prefer.

from dataclasses import dataclass
from functools import cached_property


@dataclass(frozen=True)
class Person:
    name: str
    age: int

    def __eq__(self, other):
        """Compare with a case-insensitive match on the `name` field."""
        if not isinstance(other, Person):
            return False

        return self.name_to_compare == other.name_to_compare

    @cached_property
    def name_to_compare(self):
        return self.name.casefold()

Code for testing:

if __name__ == '__main__':
    d1 = {'name': 'John Smith', 'age': 36}
    d2 = {'name': 'john smith', 'age': 36}

    p1 = Person(**d1)
    p2 = Person(**d2)

    if p1 == p2:
        print('Hooray!')
        # access rest of attributes on class object instead of dict
        # print('Person 1 name:', p1.name)

Upvotes: 1

j1-lee
j1-lee

Reputation: 13939

How about the following:

d1 = {'name':'John Smith', 'age':36}
d2 = {'name':'john smith', 'age':36}

def regularize(d):
    return {k: (v.casefold() if isinstance(v, str) else v) for k, v in d.items()}

if regularize(d1) == regularize(d2):
    print('hello!')

This converts only the string values into lower case.

Update: If only the key 'name' needs to be casefolded, then:

d1 = {'name':'John Smith', 'age':36}
d2 = {'name':'john smith', 'age':36}

def regularize(d):
    return {k: (v.casefold() if k == 'name' else v) for k, v in d.items()}

if regularize(d1) == regularize(d2):
    print('hello!')

Upvotes: 3

Related Questions