diff --git a/CHANGELOG.md b/CHANGELOG.md index 95e20d2..a873ae8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [3.8.1] - 2020-09-25 + +### Fixed +- Rearrangement of equal array items is corrupting data by redundant replaces. + ## [3.8.0] - 2020-09-25 ### Added @@ -50,7 +55,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Compatibility option to `TOLERATE_ASSOCIATIVE_ARRAYS` that mimic JSON objects. -[3.7.6]: https://github.com/swaggest/json-diff/compare/v3.7.5...v3.7.6 +[3.8.1]: https://github.com/swaggest/json-diff/compare/v3.8.0...v3.8.1 +[3.8.0]: https://github.com/swaggest/json-diff/compare/v3.7.5...v3.8.0 [3.7.5]: https://github.com/swaggest/json-diff/compare/v3.7.4...v3.7.5 [3.7.4]: https://github.com/swaggest/json-diff/compare/v3.7.3...v3.7.4 [3.7.3]: https://github.com/swaggest/json-diff/compare/v3.7.2...v3.7.3 diff --git a/src/JsonDiff.php b/src/JsonDiff.php index 2f55305..dfd3c90 100644 --- a/src/JsonDiff.php +++ b/src/JsonDiff.php @@ -503,26 +503,40 @@ private function rearrangeEqualItems(array $original, array $new) $origIdx = []; foreach ($original as $i => $item) { - $origIdx[$i] = $this->jsonHashOriginal->xorHash($item); + $hash = $this->jsonHashOriginal->xorHash($item); + $origIdx[$hash][] = $i; } $newIdx = []; foreach ($new as $i => $item) { $hash = $this->jsonHashNew->xorHash($item); - $newIdx[$hash][] = $i; + $newIdx[$i] = $hash; } - $rearranged = $new; - foreach ($origIdx as $i => $hash) { - if (empty($newIdx[$hash])) { - continue; + $newRearranged = []; + $changedItems = []; + foreach ($newIdx as $i => $hash) { + if (!empty($origIdx[$hash])) { + $j = array_shift($origIdx[$hash]); + + $newRearranged[$j] = $new[$i]; + } else { + $changedItems []= $new[$i]; } - $j = array_shift($newIdx[$hash]); - $rearranged[$i] = $new[$j]; - $rearranged[$j] = $new[$i]; } - return $rearranged; + $idx = 0; + foreach ($changedItems as $item) { + while (array_key_exists($idx, $newRearranged)) { + $idx++; + } + $newRearranged[$idx] = $item; + } + + ksort($newRearranged); + $newRearranged = array_values($newRearranged); + + return $newRearranged; } } \ No newline at end of file diff --git a/tests/src/RearrangeArrayTest.php b/tests/src/RearrangeArrayTest.php index a19eb90..a115503 100644 --- a/tests/src/RearrangeArrayTest.php +++ b/tests/src/RearrangeArrayTest.php @@ -210,4 +210,178 @@ public function testExample() $this->assertEquals('[{"value":165,"op":"test","path":"/2/height"},{"value":168,"op":"replace","path":"/2/height"}]', json_encode($diff->getPatch(), JSON_UNESCAPED_SLASHES)); } + + public function testReplacement() + { + $ex1 = json_decode(<<<'JSON' +{ + "attribute": { + "name": ".UpwardPropagation - Prescriptions - Log-Ranges", + "attribute": ".UpwardPropagation - Prescriptions - Log-Ranges", + "dimension": ".UpwardPropagation - Prescriptions", + "object": "Patients" + }, + "selectedStates": [ + "]200,500]", + "]500,1000]", + "]20,50]", + "]100,200]", + "]5000,10000]", + "]5,10]", + "]1,2]", + "]10,20]", + "null", + "]10000,oo[", + "]2,5]", + "]0,1]", + "]1000,2000]", + "]50,100]", + "]2000,5000]" + ] +} +JSON + ); + + $ex2 = json_decode(<<<'JSON' +{ + "attribute": { + "name": ".UpwardPropagation - Prescriptions - Log-Ranges", + "attribute": ".UpwardPropagation - Prescriptions - Log-Ranges", + "dimension": ".UpwardPropagation - Prescriptions", + "object": "Patients" + }, + "selectedStates": [ + "]2000,5000]", + "]2,5]", + "]20,50]", + "]1,2]", + "]10000,oo[", + "]200,500]", + "]50,100]", + "]500,1000]", + "]5,10]", + "]10,20]", + "null", + "]0,1]", + "]1000,2000]", + "]5000,10000]", + "]100,200]" + ] +} +JSON + ); + + $diff = new JsonDiff($ex1, $ex2, JsonDiff::REARRANGE_ARRAYS); + $ex2r = $diff->getRearranged(); + $missingItems = []; + foreach ($ex2->selectedStates as $i => $item) { + if (!in_array($item, $ex2r->selectedStates)) { + $missingItems[$i] = $item; + } + } + + $this->assertEmpty($missingItems, json_encode($ex2r, JSON_UNESCAPED_SLASHES)); + $this->assertEquals( + json_encode($ex1, JSON_UNESCAPED_SLASHES+JSON_PRETTY_PRINT), + json_encode($ex2r, JSON_UNESCAPED_SLASHES+JSON_PRETTY_PRINT) + ); + } + + public function testReplacementChanges() + { + $ex1 = json_decode(<<<'JSON' +{ + "attribute": { + "name": ".UpwardPropagation - Prescriptions - Log-Ranges", + "attribute": ".UpwardPropagation - Prescriptions - Log-Ranges", + "dimension": ".UpwardPropagation - Prescriptions", + "object": "Patients" + }, + "selectedStates": [ + "]200,500]", + "]500,1000]", + "]100,200]", + "]5000,10000]", + "]5,10]", + "]1,2]", + "]10,20]", + "null", + "]10000,oo[", + "]2,5]", + "]0,1]", + "]1000,2000]", + "]50,100]", + "]2000,5000]" + ] +} +JSON + ); + + $ex2 = json_decode(<<<'JSON' +{ + "attribute": { + "name": ".UpwardPropagation - Prescriptions - Log-Ranges", + "attribute": ".UpwardPropagation - Prescriptions - Log-Ranges", + "dimension": ".UpwardPropagation - Prescriptions", + "object": "Patients" + }, + "selectedStates": [ + "]2000,5000]", + "]2,5]", + "]20,50]", + "]1,2]", + "]10000,oo[", + "]200,500]", + "]50,100]", + "]500,1000]", + "]5,10]", + "]10,20]", + "]0,1]", + "]1000,2000]", + "]5000,10000]", + "]100,200]" + ] +} +JSON + ); + + $diff = new JsonDiff($ex1, $ex2, JsonDiff::REARRANGE_ARRAYS); + $ex2r = $diff->getRearranged(); + $missingItems = []; + foreach ($ex2->selectedStates as $i => $item) { + if (!in_array($item, $ex2r->selectedStates)) { + $missingItems[$i] = $item; + } + } + + $this->assertEmpty($missingItems, json_encode($ex2r, JSON_UNESCAPED_SLASHES)); + $this->assertEquals( + '{ + "attribute": { + "name": ".UpwardPropagation - Prescriptions - Log-Ranges", + "attribute": ".UpwardPropagation - Prescriptions - Log-Ranges", + "dimension": ".UpwardPropagation - Prescriptions", + "object": "Patients" + }, + "selectedStates": [ + "]200,500]", + "]500,1000]", + "]100,200]", + "]5000,10000]", + "]5,10]", + "]1,2]", + "]10,20]", + "]20,50]", + "]10000,oo[", + "]2,5]", + "]0,1]", + "]1000,2000]", + "]50,100]", + "]2000,5000]" + ] +}', + json_encode($ex2r, JSON_UNESCAPED_SLASHES+JSON_PRETTY_PRINT) + ); + } + }