cdgriffith / Box

Python dictionaries with advanced dot notation access
https://github.com/cdgriffith/Box/wiki
MIT License
2.61k stars 106 forks source link

Feature: Compare 2 or more box #144

Closed hitengajjar closed 4 years ago

hitengajjar commented 4 years ago

I tried and failed to look to have a compare functionality that can provide a report of what's different between 2 boxes. I notice I can compare 2 Boxes with a comparison operator e.g. Box(a=1, b=dict(c=1)) == Box(a=1, b=dict(c=1)) however it would be nice to have a special Box.compare(box2,...) method that also generates a report (maybe a Box object itself)

cdgriffith commented 4 years ago

That is a great idea. I would want to avoid adding another method, especially with that common of a word, however I think it might be good to just have a - minus ability, like it currently has the addition ability.

rough mockup would look like:

    def __sub__(self, other):
        output = self.__class__(**self.__box_config())
        for item in self:
            if item not in other:
                output[item] = self[item]
            elif isinstance(self.get(item), dict) and isinstance(other.get(item), dict):
                output[item] = self[item] - other[item]
        return output

Returning something like:

from box import Box
box_1 = Box(a={'1': 1, '2': '2'}, b='b', c='c')
box_2 = Box(a={'1': 'different_value'}, b='b')
box_1 - box_2
# Out[2]: <Box: {'a': {'2': '2'}, 'c': 'c'}>

This would all be key based. To do a compare that checked actual value differences will have to think of some way to be able to present that information back. Possibly putting conflicting values into a namedtuple or something.

hitengajjar commented 4 years ago

Hey Chris, Thanks for considering this as a feature. Though your suggestion looks reasonable, still it doesn't exactly provide a report on what's the exact difference between 2 or more boxes (and think this is unique feature that doesn't exist in other dict based libraries I know).

Just to be sure you mean - operator will act as it works on set(). Is that correct? If yes, then I don't think value difference should be presented in tuple form. Maybe, in such case output key-value pair from the first box in operation.

cdgriffith commented 4 years ago

Just found a dictionary comparison solution exists called dictdiffer (readthedocs for it) in case you were looking for a fast solution.

Quick test run with box seems fine:

In [5]: from dictdiffer import diff

In [6]: a = Box(test_dict)

In [7]: b = Box(test_dict, another_key=2)

In [8]: diff(a, b)
Out[8]: <generator object diff.<locals>._diff_recursive at 0x077735F0>

In [10]: list(diff(a,b))
Out[10]: [('add', '', [('another_key', 2)])]

In [11]: b
Out[11]: <Box: {'key1': 'value1', 'not$allowed': 'fine_value', 'BigCamel': 'hi', 'alist': [{'a': 1}], 'Key 2': {'Key 3': 'Value 3', 'Key4': {'Key5': 'Value5'}}, 'another_key': 2}>

In [12]: b.key1 = 5

In [13]: list(diff(a,b))
Out[13]: [('change', 'key1', ('value1', 5)), ('add', '', [('another_key', 2)])]