DataDiffer.js 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. /**
  20. * AUTO-GENERATED FILE. DO NOT MODIFY.
  21. */
  22. /*
  23. * Licensed to the Apache Software Foundation (ASF) under one
  24. * or more contributor license agreements. See the NOTICE file
  25. * distributed with this work for additional information
  26. * regarding copyright ownership. The ASF licenses this file
  27. * to you under the Apache License, Version 2.0 (the
  28. * "License"); you may not use this file except in compliance
  29. * with the License. You may obtain a copy of the License at
  30. *
  31. * http://www.apache.org/licenses/LICENSE-2.0
  32. *
  33. * Unless required by applicable law or agreed to in writing,
  34. * software distributed under the License is distributed on an
  35. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  36. * KIND, either express or implied. See the License for the
  37. * specific language governing permissions and limitations
  38. * under the License.
  39. */
  40. function dataIndexMapValueLength(valNumOrArrLengthMoreThan2) {
  41. return valNumOrArrLengthMoreThan2 == null ? 0 : valNumOrArrLengthMoreThan2.length || 1;
  42. }
  43. function defaultKeyGetter(item) {
  44. return item;
  45. }
  46. var DataDiffer = /** @class */function () {
  47. /**
  48. * @param context Can be visited by this.context in callback.
  49. */
  50. function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context,
  51. // By default: 'oneToOne'.
  52. diffMode) {
  53. this._old = oldArr;
  54. this._new = newArr;
  55. this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;
  56. this._newKeyGetter = newKeyGetter || defaultKeyGetter;
  57. // Visible in callback via `this.context`;
  58. this.context = context;
  59. this._diffModeMultiple = diffMode === 'multiple';
  60. }
  61. /**
  62. * Callback function when add a data
  63. */
  64. DataDiffer.prototype.add = function (func) {
  65. this._add = func;
  66. return this;
  67. };
  68. /**
  69. * Callback function when update a data
  70. */
  71. DataDiffer.prototype.update = function (func) {
  72. this._update = func;
  73. return this;
  74. };
  75. /**
  76. * Callback function when update a data and only work in `cbMode: 'byKey'`.
  77. */
  78. DataDiffer.prototype.updateManyToOne = function (func) {
  79. this._updateManyToOne = func;
  80. return this;
  81. };
  82. /**
  83. * Callback function when update a data and only work in `cbMode: 'byKey'`.
  84. */
  85. DataDiffer.prototype.updateOneToMany = function (func) {
  86. this._updateOneToMany = func;
  87. return this;
  88. };
  89. /**
  90. * Callback function when update a data and only work in `cbMode: 'byKey'`.
  91. */
  92. DataDiffer.prototype.updateManyToMany = function (func) {
  93. this._updateManyToMany = func;
  94. return this;
  95. };
  96. /**
  97. * Callback function when remove a data
  98. */
  99. DataDiffer.prototype.remove = function (func) {
  100. this._remove = func;
  101. return this;
  102. };
  103. DataDiffer.prototype.execute = function () {
  104. this[this._diffModeMultiple ? '_executeMultiple' : '_executeOneToOne']();
  105. };
  106. DataDiffer.prototype._executeOneToOne = function () {
  107. var oldArr = this._old;
  108. var newArr = this._new;
  109. var newDataIndexMap = {};
  110. var oldDataKeyArr = new Array(oldArr.length);
  111. var newDataKeyArr = new Array(newArr.length);
  112. this._initIndexMap(oldArr, null, oldDataKeyArr, '_oldKeyGetter');
  113. this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter');
  114. for (var i = 0; i < oldArr.length; i++) {
  115. var oldKey = oldDataKeyArr[i];
  116. var newIdxMapVal = newDataIndexMap[oldKey];
  117. var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal);
  118. // idx can never be empty array here. see 'set null' logic below.
  119. if (newIdxMapValLen > 1) {
  120. // Consider there is duplicate key (for example, use dataItem.name as key).
  121. // We should make sure every item in newArr and oldArr can be visited.
  122. var newIdx = newIdxMapVal.shift();
  123. if (newIdxMapVal.length === 1) {
  124. newDataIndexMap[oldKey] = newIdxMapVal[0];
  125. }
  126. this._update && this._update(newIdx, i);
  127. } else if (newIdxMapValLen === 1) {
  128. newDataIndexMap[oldKey] = null;
  129. this._update && this._update(newIdxMapVal, i);
  130. } else {
  131. this._remove && this._remove(i);
  132. }
  133. }
  134. this._performRestAdd(newDataKeyArr, newDataIndexMap);
  135. };
  136. /**
  137. * For example, consider the case:
  138. * oldData: [o0, o1, o2, o3, o4, o5, o6, o7],
  139. * newData: [n0, n1, n2, n3, n4, n5, n6, n7, n8],
  140. * Where:
  141. * o0, o1, n0 has key 'a' (many to one)
  142. * o5, n4, n5, n6 has key 'b' (one to many)
  143. * o2, n1 has key 'c' (one to one)
  144. * n2, n3 has key 'd' (add)
  145. * o3, o4 has key 'e' (remove)
  146. * o6, o7, n7, n8 has key 'f' (many to many, treated as add and remove)
  147. * Then:
  148. * (The order of the following directives are not ensured.)
  149. * this._updateManyToOne(n0, [o0, o1]);
  150. * this._updateOneToMany([n4, n5, n6], o5);
  151. * this._update(n1, o2);
  152. * this._remove(o3);
  153. * this._remove(o4);
  154. * this._remove(o6);
  155. * this._remove(o7);
  156. * this._add(n2);
  157. * this._add(n3);
  158. * this._add(n7);
  159. * this._add(n8);
  160. */
  161. DataDiffer.prototype._executeMultiple = function () {
  162. var oldArr = this._old;
  163. var newArr = this._new;
  164. var oldDataIndexMap = {};
  165. var newDataIndexMap = {};
  166. var oldDataKeyArr = [];
  167. var newDataKeyArr = [];
  168. this._initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter');
  169. this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter');
  170. for (var i = 0; i < oldDataKeyArr.length; i++) {
  171. var oldKey = oldDataKeyArr[i];
  172. var oldIdxMapVal = oldDataIndexMap[oldKey];
  173. var newIdxMapVal = newDataIndexMap[oldKey];
  174. var oldIdxMapValLen = dataIndexMapValueLength(oldIdxMapVal);
  175. var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal);
  176. if (oldIdxMapValLen > 1 && newIdxMapValLen === 1) {
  177. this._updateManyToOne && this._updateManyToOne(newIdxMapVal, oldIdxMapVal);
  178. newDataIndexMap[oldKey] = null;
  179. } else if (oldIdxMapValLen === 1 && newIdxMapValLen > 1) {
  180. this._updateOneToMany && this._updateOneToMany(newIdxMapVal, oldIdxMapVal);
  181. newDataIndexMap[oldKey] = null;
  182. } else if (oldIdxMapValLen === 1 && newIdxMapValLen === 1) {
  183. this._update && this._update(newIdxMapVal, oldIdxMapVal);
  184. newDataIndexMap[oldKey] = null;
  185. } else if (oldIdxMapValLen > 1 && newIdxMapValLen > 1) {
  186. this._updateManyToMany && this._updateManyToMany(newIdxMapVal, oldIdxMapVal);
  187. newDataIndexMap[oldKey] = null;
  188. } else if (oldIdxMapValLen > 1) {
  189. for (var i_1 = 0; i_1 < oldIdxMapValLen; i_1++) {
  190. this._remove && this._remove(oldIdxMapVal[i_1]);
  191. }
  192. } else {
  193. this._remove && this._remove(oldIdxMapVal);
  194. }
  195. }
  196. this._performRestAdd(newDataKeyArr, newDataIndexMap);
  197. };
  198. DataDiffer.prototype._performRestAdd = function (newDataKeyArr, newDataIndexMap) {
  199. for (var i = 0; i < newDataKeyArr.length; i++) {
  200. var newKey = newDataKeyArr[i];
  201. var newIdxMapVal = newDataIndexMap[newKey];
  202. var idxMapValLen = dataIndexMapValueLength(newIdxMapVal);
  203. if (idxMapValLen > 1) {
  204. for (var j = 0; j < idxMapValLen; j++) {
  205. this._add && this._add(newIdxMapVal[j]);
  206. }
  207. } else if (idxMapValLen === 1) {
  208. this._add && this._add(newIdxMapVal);
  209. }
  210. // Support both `newDataKeyArr` are duplication removed or not removed.
  211. newDataIndexMap[newKey] = null;
  212. }
  213. };
  214. DataDiffer.prototype._initIndexMap = function (arr,
  215. // Can be null.
  216. map,
  217. // In 'byKey', the output `keyArr` is duplication removed.
  218. // In 'byIndex', the output `keyArr` is not duplication removed and
  219. // its indices are accurately corresponding to `arr`.
  220. keyArr, keyGetterName) {
  221. var cbModeMultiple = this._diffModeMultiple;
  222. for (var i = 0; i < arr.length; i++) {
  223. // Add prefix to avoid conflict with Object.prototype.
  224. var key = '_ec_' + this[keyGetterName](arr[i], i);
  225. if (!cbModeMultiple) {
  226. keyArr[i] = key;
  227. }
  228. if (!map) {
  229. continue;
  230. }
  231. var idxMapVal = map[key];
  232. var idxMapValLen = dataIndexMapValueLength(idxMapVal);
  233. if (idxMapValLen === 0) {
  234. // Simple optimize: in most cases, one index has one key,
  235. // do not need array.
  236. map[key] = i;
  237. if (cbModeMultiple) {
  238. keyArr.push(key);
  239. }
  240. } else if (idxMapValLen === 1) {
  241. map[key] = [idxMapVal, i];
  242. } else {
  243. idxMapVal.push(i);
  244. }
  245. }
  246. };
  247. return DataDiffer;
  248. }();
  249. export default DataDiffer;