DataDiffer.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  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 =
  47. /** @class */
  48. function () {
  49. /**
  50. * @param context Can be visited by this.context in callback.
  51. */
  52. function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context, // By default: 'oneToOne'.
  53. diffMode) {
  54. this._old = oldArr;
  55. this._new = newArr;
  56. this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;
  57. this._newKeyGetter = newKeyGetter || defaultKeyGetter; // 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); // idx can never be empty array here. see 'set null' logic below.
  118. if (newIdxMapValLen > 1) {
  119. // Consider there is duplicate key (for example, use dataItem.name as key).
  120. // We should make sure every item in newArr and oldArr can be visited.
  121. var newIdx = newIdxMapVal.shift();
  122. if (newIdxMapVal.length === 1) {
  123. newDataIndexMap[oldKey] = newIdxMapVal[0];
  124. }
  125. this._update && this._update(newIdx, i);
  126. } else if (newIdxMapValLen === 1) {
  127. newDataIndexMap[oldKey] = null;
  128. this._update && this._update(newIdxMapVal, i);
  129. } else {
  130. this._remove && this._remove(i);
  131. }
  132. }
  133. this._performRestAdd(newDataKeyArr, newDataIndexMap);
  134. };
  135. /**
  136. * For example, consider the case:
  137. * oldData: [o0, o1, o2, o3, o4, o5, o6, o7],
  138. * newData: [n0, n1, n2, n3, n4, n5, n6, n7, n8],
  139. * Where:
  140. * o0, o1, n0 has key 'a' (many to one)
  141. * o5, n4, n5, n6 has key 'b' (one to many)
  142. * o2, n1 has key 'c' (one to one)
  143. * n2, n3 has key 'd' (add)
  144. * o3, o4 has key 'e' (remove)
  145. * o6, o7, n7, n8 has key 'f' (many to many, treated as add and remove)
  146. * Then:
  147. * (The order of the following directives are not ensured.)
  148. * this._updateManyToOne(n0, [o0, o1]);
  149. * this._updateOneToMany([n4, n5, n6], o5);
  150. * this._update(n1, o2);
  151. * this._remove(o3);
  152. * this._remove(o4);
  153. * this._remove(o6);
  154. * this._remove(o7);
  155. * this._add(n2);
  156. * this._add(n3);
  157. * this._add(n7);
  158. * this._add(n8);
  159. */
  160. DataDiffer.prototype._executeMultiple = function () {
  161. var oldArr = this._old;
  162. var newArr = this._new;
  163. var oldDataIndexMap = {};
  164. var newDataIndexMap = {};
  165. var oldDataKeyArr = [];
  166. var newDataKeyArr = [];
  167. this._initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter');
  168. this._initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter');
  169. for (var i = 0; i < oldDataKeyArr.length; i++) {
  170. var oldKey = oldDataKeyArr[i];
  171. var oldIdxMapVal = oldDataIndexMap[oldKey];
  172. var newIdxMapVal = newDataIndexMap[oldKey];
  173. var oldIdxMapValLen = dataIndexMapValueLength(oldIdxMapVal);
  174. var newIdxMapValLen = dataIndexMapValueLength(newIdxMapVal);
  175. if (oldIdxMapValLen > 1 && newIdxMapValLen === 1) {
  176. this._updateManyToOne && this._updateManyToOne(newIdxMapVal, oldIdxMapVal);
  177. newDataIndexMap[oldKey] = null;
  178. } else if (oldIdxMapValLen === 1 && newIdxMapValLen > 1) {
  179. this._updateOneToMany && this._updateOneToMany(newIdxMapVal, oldIdxMapVal);
  180. newDataIndexMap[oldKey] = null;
  181. } else if (oldIdxMapValLen === 1 && newIdxMapValLen === 1) {
  182. this._update && this._update(newIdxMapVal, oldIdxMapVal);
  183. newDataIndexMap[oldKey] = null;
  184. } else if (oldIdxMapValLen > 1 && newIdxMapValLen > 1) {
  185. this._updateManyToMany && this._updateManyToMany(newIdxMapVal, oldIdxMapVal);
  186. newDataIndexMap[oldKey] = null;
  187. } else if (oldIdxMapValLen > 1) {
  188. for (var i_1 = 0; i_1 < oldIdxMapValLen; i_1++) {
  189. this._remove && this._remove(oldIdxMapVal[i_1]);
  190. }
  191. } else {
  192. this._remove && this._remove(oldIdxMapVal);
  193. }
  194. }
  195. this._performRestAdd(newDataKeyArr, newDataIndexMap);
  196. };
  197. DataDiffer.prototype._performRestAdd = function (newDataKeyArr, newDataIndexMap) {
  198. for (var i = 0; i < newDataKeyArr.length; i++) {
  199. var newKey = newDataKeyArr[i];
  200. var newIdxMapVal = newDataIndexMap[newKey];
  201. var idxMapValLen = dataIndexMapValueLength(newIdxMapVal);
  202. if (idxMapValLen > 1) {
  203. for (var j = 0; j < idxMapValLen; j++) {
  204. this._add && this._add(newIdxMapVal[j]);
  205. }
  206. } else if (idxMapValLen === 1) {
  207. this._add && this._add(newIdxMapVal);
  208. } // Support both `newDataKeyArr` are duplication removed or not removed.
  209. newDataIndexMap[newKey] = null;
  210. }
  211. };
  212. DataDiffer.prototype._initIndexMap = function (arr, // Can be null.
  213. map, // In 'byKey', the output `keyArr` is duplication removed.
  214. // In 'byIndex', the output `keyArr` is not duplication removed and
  215. // its indices are accurately corresponding to `arr`.
  216. keyArr, keyGetterName) {
  217. var cbModeMultiple = this._diffModeMultiple;
  218. for (var i = 0; i < arr.length; i++) {
  219. // Add prefix to avoid conflict with Object.prototype.
  220. var key = '_ec_' + this[keyGetterName](arr[i], i);
  221. if (!cbModeMultiple) {
  222. keyArr[i] = key;
  223. }
  224. if (!map) {
  225. continue;
  226. }
  227. var idxMapVal = map[key];
  228. var idxMapValLen = dataIndexMapValueLength(idxMapVal);
  229. if (idxMapValLen === 0) {
  230. // Simple optimize: in most cases, one index has one key,
  231. // do not need array.
  232. map[key] = i;
  233. if (cbModeMultiple) {
  234. keyArr.push(key);
  235. }
  236. } else if (idxMapValLen === 1) {
  237. map[key] = [idxMapVal, i];
  238. } else {
  239. idxMapVal.push(i);
  240. }
  241. }
  242. };
  243. return DataDiffer;
  244. }();
  245. export default DataDiffer;