Home > Software engineering >  check almost equals in deeply nested dicts with arrays (please read the desc)
check almost equals in deeply nested dicts with arrays (please read the desc)

Time:09-29

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?

CodePudding user response:

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) # {}

CodePudding user response:

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)  
  • Related