swaggest / json-diff

JSON diff/rearrange/patch/pointer library for PHP
MIT License
220 stars 30 forks source link

Difference ignoring order of elements in an array. #33

Closed webpunk closed 4 years ago

webpunk commented 4 years ago

Hi,

is there a way to get the difference between two JSON objects while ignoring the order of elements in an array. For instance, the following example currently returns 4 differences but should return 0.

$diff = new \Swaggest\JsonDiff\JsonDiff( json_decode('{"data": [{"A": 1},{"B": 2}]}'), json_decode('{"data": [{"B": 2},{"A": 1}]}'), \Swaggest\JsonDiff\JsonDiff::REARRANGE_ARRAYS ); echo "\n" . $diff->getDiffCnt() . "\n";

Thanks!

vearutop commented 4 years ago

Hello, REARRANGE_ARRAYS is a best effort feature that targets homogenic arrays. In order to be rearranged, both arrays have to have at least one key that is avaialble in all items and that key needs to have unique values in original array.

If your values would have beed like these:

{"data": [{"A": 1, "C": 1},{"B": 2, "C": 2}]}
{"data": [{"B": 2, "C": 2},{"A": 1, "C": 1}]}

they would produce empty diff.

The reason for such an approach is to reduce computational complexity, and main usecase for the initial implementation was to address non-semantic differences in swagger parameters.

Solving general case like yours would need some quadratic recursive comparisons to understand that a particular array item is equal to another one. Is this case important for you? I think it can be implemented as some additional option with a performance impact disclaimer.

webpunk commented 4 years ago

Thanks for the swift reply! Unfortunately, I can't influence the format of the JSON object because it is provided by an API. So this would actually be an important feature for me. Maybe I can customize the code of JSONDiff if you show me where to find the corresponding code parts?

vearutop commented 4 years ago

@webpunk I have an idea how to implement rearrangement based on elements equality with reasonable performance, optimistically will make it in couple of days.

webpunk commented 4 years ago

@vearutop yes, that would be great! Please, let me know if I can help...

vearutop commented 4 years ago

@webpunk please check if new v3.8.0 works for you. No new option needed, JsonDiff::REARRANGE_ARRAYS should suffice.

webpunk commented 4 years ago

Hi @vearutop ! Thanks a lot for the new version. I have just tested it. For the following two JSON objects, JSONDiff still reports a difference that I can't see.

Example.zip

vearutop commented 4 years ago

@webpunk thanks for the test case, please check v3.8.1.

webpunk commented 4 years ago

@vearutop unfortunately, there still seems to be a problem with an array of strings. I've attached another example. Thx! Example2.zip

vearutop commented 4 years ago

@webpunk I've tried this example with a test case:

    public function testExample2() {
        $ex1 = json_decode(file_get_contents(__DIR__ . '/../assets/Example1.json'));
        $ex2 = json_decode(file_get_contents(__DIR__ . '/../assets/Example2.json'));

        $diff = new JsonDiff($ex1, $ex2, JsonDiff::REARRANGE_ARRAYS);
        $this->assertEquals(0, $diff->getDiffCnt());
    }

and the test passed (no differences found).

Could you elaborate the problem that you are seeing with array of strings, or maybe steps to reproduce?

webpunk commented 4 years ago

@vearutop Sorry... that was a mistake on my part! Now it works perfectly. Thanks a lot!

melnikaite commented 1 year ago

Sometimes ordering of fields name and code swaps, having REARRANGE_ARRAYS it supposed to be ignored, but in fact treated as difference

{
    "id": 826907,
    "contracts": [
        {
            "id": 715559,
            "product": {
                "name": "Friends",
                "code": "friends"
            }
        }
    ]
}
code ``` getPatch()->jsonSerialize()); echo $patch; ```