2

suppose I got two nested dicts with arrays of dicts inside, i want to check if the values are close enough. == doesnt work since it doesnt check for array values
extending the ApproxMapping class doesnt work either

dict1 = {
'a': 1, 
'b': [
      {'c': [{'d': 32.069},{'e': 32.420}]}
     ]
}
dict2 = {
'a': 1, 
'b': [
      {'c': [{'d': 32.070},{'e': 32.421}]}
     ]
}

How can I check they are almost equal, is there anyway i can override the pytest.approx method to work for nested dicts and arrays?

amitava mozumder
  • 788
  • 6
  • 17

2 Answers2

2

Checkout the deepdiff library:

from deepdiff import DeepDiff

dict1 = {
'a': 1, 
'b': [
      {'c': [{'d': 32.069},{'e': 32.420}]}
     ]
}
dict2 = {
'a': 1, 
'b': [
      {'c': [{'d': 32.070},{'e': 32.421}]}
     ]
}

diff = DeepDiff(dict1, dict2, significant_digits=2)
print(diff) # {}
Andrey Ilin
  • 61
  • 1
  • 4
  • thanks, all the pytest methods failed, I have been trying to extend those methods and wrote so much unnecessary code, this works like a charm. – amitava mozumder Sep 28 '21 at 20:06
2

Here's a pure Python approach, if you want one:

def assert_values_almost_equal(value1, value2, tolerance=1e-5):
    if isinstance(value1, dict):
        assert_dicts_almost_equal(value1, value2, tolerance=tolerance)
    elif isinstance(value1, list):
        assert_sequences_almost_equal(value1, value2, tolerance=tolerance)
    elif isinstance(value1, (int, float)):
        assert_numbers_almost_equal(value1, value2, tolerance=tolerance)
    else:
        assert value1 == value2, f'Value 1: {n1} != Value 2: {n2}'

def assert_dicts_almost_equal(d1, d2, tolerance=1e-5):
    for (k1, v1), (k2, v2) in zip(d1.items(), d2.items()):
        assert_values_almost_equal(k1, k2, tolerance=tolerance)
        assert_values_almost_equal(v1, v2, tolerance=tolerance)

def assert_sequences_almost_equal(s1, s2, tolerance=1e-5):
    for e1, e2 in zip(s1, s2):
        assert_values_almost_equal(e1, e2, tolerance=tolerance)

def assert_numbers_almost_equal(n1, n2, tolerance=1e-5):
    assert abs(n1 - n2) < tolerance, f'Number 1: {n1} != Number 2: {n2}'

Note that if you want to test other data types not covered in assert_values_almost_equal (e.g. numpy arrays), you'll need to add the proper function, along with the corresponding isinstance check (use np.testing.assert_array_almost_equal for the assertion if you need to do this).

Testing:

dict1 = {
'a': 1, 
'b': [
      {'c': [{'d': 32.069},{'e': 32.420}]}
     ]
}
dict2 = {
'a': 1, 
'b': [
      {'c': [{'d': 32.070},{'e': 32.421}]}
     ]
}

# passes
assert_dicts_almost_equal(dict1, dict2, tolerance=0.1)

# AssertionError: Number 1: 32.069 != Number 2: 32.07
assert_dicts_almost_equal(dict1, dict2, tolerance=0.0001)  
jfaccioni
  • 7,099
  • 1
  • 9
  • 25