From 3779668f0574b6a814c0c300b02d556b196ea0a6 Mon Sep 17 00:00:00 2001 From: Vitaliy <160263432+vitalivo@users.noreply.github.com> Date: Sat, 12 Sep 2026 18:22:28 +0300 Subject: [PATCH] Hash unordered containers without comparing their members --- dictdiffer/utils.py | 5 +++-- tests/test_utils.py | 11 +++++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/dictdiffer/utils.py b/dictdiffer/utils.py index 3194f9f..034eecd 100644 --- a/dictdiffer/utils.py +++ b/dictdiffer/utils.py @@ -201,9 +201,10 @@ def nested_hash(obj): if isinstance(obj, (list, tuple)): return hash(tuple(map(nested_hash, obj))) elif isinstance(obj, set): - return hash(tuple(map(nested_hash, sorted(obj)))) + return hash(frozenset(obj)) elif isinstance(obj, dict): - return hash(tuple(map(nested_hash, sorted(obj.items())))) + return hash(frozenset((key, nested_hash(value)) + for key, value in obj.items())) def dot_lookup(source, lookup, parent=False): diff --git a/tests/test_utils.py b/tests/test_utils.py index fd2cbd9..fc84e76 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -146,3 +146,14 @@ def test_nested_hash(self): nested_hash((1, 2, 3)) nested_hash(set([1, 2, 3])) nested_hash({'foo': 'bar'}) + + + def test_nested_hash_mixed_set(self): + first = {1, 'one', (2, 3)} + second = {(2, 3), 'one', 1} + self.assertEqual(nested_hash(first), nested_hash(second)) + + def test_nested_hash_mixed_dict_keys(self): + first = {1: ['one'], 'two': {3, 'three'}} + second = {'two': {'three', 3}, 1: ['one']} + self.assertEqual(nested_hash(first), nested_hash(second))