arrayDiff.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. function diff(oldArr, newArr, equals) {
  2. if (!equals) {
  3. equals = function (a, b) {
  4. return a === b;
  5. };
  6. }
  7. oldArr = oldArr.slice();
  8. newArr = newArr.slice();
  9. var newLen = newArr.length;
  10. var oldLen = oldArr.length;
  11. var editLength = 1;
  12. var maxEditLength = newLen + oldLen;
  13. var bestPath = [{ newPos: -1, components: [] }];
  14. var oldPos = extractCommon(bestPath[0], newArr, oldArr, 0, equals);
  15. if (bestPath[0].newPos + 1 >= newLen && oldPos + 1 >= oldLen) {
  16. var indices = [];
  17. for (var i = 0; i < newArr.length; i++) {
  18. indices.push(i);
  19. }
  20. return [{
  21. indices: indices,
  22. count: newArr.length,
  23. added: false,
  24. removed: false
  25. }];
  26. }
  27. function execEditLength() {
  28. for (var diagonalPath = -1 * editLength; diagonalPath <= editLength; diagonalPath += 2) {
  29. var basePath;
  30. var addPath = bestPath[diagonalPath - 1];
  31. var removePath = bestPath[diagonalPath + 1];
  32. var oldPos = (removePath ? removePath.newPos : 0) - diagonalPath;
  33. if (addPath) {
  34. bestPath[diagonalPath - 1] = undefined;
  35. }
  36. var canAdd = addPath && addPath.newPos + 1 < newLen;
  37. var canRemove = removePath && 0 <= oldPos && oldPos < oldLen;
  38. if (!canAdd && !canRemove) {
  39. bestPath[diagonalPath] = undefined;
  40. continue;
  41. }
  42. if (!canAdd || (canRemove && addPath.newPos < removePath.newPos)) {
  43. basePath = clonePath(removePath);
  44. pushComponent(basePath.components, false, true);
  45. }
  46. else {
  47. basePath = addPath;
  48. basePath.newPos++;
  49. pushComponent(basePath.components, true, false);
  50. }
  51. oldPos = extractCommon(basePath, newArr, oldArr, diagonalPath, equals);
  52. if (basePath.newPos + 1 >= newLen && oldPos + 1 >= oldLen) {
  53. return buildValues(basePath.components);
  54. }
  55. else {
  56. bestPath[diagonalPath] = basePath;
  57. }
  58. }
  59. editLength++;
  60. }
  61. while (editLength <= maxEditLength) {
  62. var ret = execEditLength();
  63. if (ret) {
  64. return ret;
  65. }
  66. }
  67. }
  68. function extractCommon(basePath, newArr, oldArr, diagonalPath, equals) {
  69. var newLen = newArr.length;
  70. var oldLen = oldArr.length;
  71. var newPos = basePath.newPos;
  72. var oldPos = newPos - diagonalPath;
  73. var commonCount = 0;
  74. while (newPos + 1 < newLen && oldPos + 1 < oldLen && equals(newArr[newPos + 1], oldArr[oldPos + 1])) {
  75. newPos++;
  76. oldPos++;
  77. commonCount++;
  78. }
  79. if (commonCount) {
  80. basePath.components.push({
  81. count: commonCount,
  82. added: false,
  83. removed: false,
  84. indices: []
  85. });
  86. }
  87. basePath.newPos = newPos;
  88. return oldPos;
  89. }
  90. function pushComponent(components, added, removed) {
  91. var last = components[components.length - 1];
  92. if (last && last.added === added && last.removed === removed) {
  93. components[components.length - 1] = {
  94. count: last.count + 1,
  95. added: added,
  96. removed: removed,
  97. indices: []
  98. };
  99. }
  100. else {
  101. components.push({
  102. count: 1,
  103. added: added,
  104. removed: removed,
  105. indices: []
  106. });
  107. }
  108. }
  109. function buildValues(components) {
  110. var componentPos = 0;
  111. var componentLen = components.length;
  112. var newPos = 0;
  113. var oldPos = 0;
  114. for (; componentPos < componentLen; componentPos++) {
  115. var component = components[componentPos];
  116. if (!component.removed) {
  117. var indices = [];
  118. for (var i = newPos; i < newPos + component.count; i++) {
  119. indices.push(i);
  120. }
  121. component.indices = indices;
  122. newPos += component.count;
  123. if (!component.added) {
  124. oldPos += component.count;
  125. }
  126. }
  127. else {
  128. for (var i = oldPos; i < oldPos + component.count; i++) {
  129. component.indices.push(i);
  130. }
  131. oldPos += component.count;
  132. }
  133. }
  134. return components;
  135. }
  136. function clonePath(path) {
  137. return { newPos: path.newPos, components: path.components.slice(0) };
  138. }
  139. export default function arrayDiff(oldArr, newArr, equal) {
  140. return diff(oldArr, newArr, equal);
  141. }