DataDiffer.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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. * Licensed to the Apache Software Foundation (ASF) under one
  21. * or more contributor license agreements. See the NOTICE file
  22. * distributed with this work for additional information
  23. * regarding copyright ownership. The ASF licenses this file
  24. * to you under the Apache License, Version 2.0 (the
  25. * "License"); you may not use this file except in compliance
  26. * with the License. You may obtain a copy of the License at
  27. *
  28. * http://www.apache.org/licenses/LICENSE-2.0
  29. *
  30. * Unless required by applicable law or agreed to in writing,
  31. * software distributed under the License is distributed on an
  32. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  33. * KIND, either express or implied. See the License for the
  34. * specific language governing permissions and limitations
  35. * under the License.
  36. */
  37. function defaultKeyGetter(item) {
  38. return item;
  39. }
  40. /**
  41. * @param {Array} oldArr
  42. * @param {Array} newArr
  43. * @param {Function} oldKeyGetter
  44. * @param {Function} newKeyGetter
  45. * @param {Object} [context] Can be visited by this.context in callback.
  46. */
  47. function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context) {
  48. this._old = oldArr;
  49. this._new = newArr;
  50. this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;
  51. this._newKeyGetter = newKeyGetter || defaultKeyGetter;
  52. this.context = context;
  53. }
  54. DataDiffer.prototype = {
  55. constructor: DataDiffer,
  56. /**
  57. * Callback function when add a data
  58. */
  59. add: function (func) {
  60. this._add = func;
  61. return this;
  62. },
  63. /**
  64. * Callback function when update a data
  65. */
  66. update: function (func) {
  67. this._update = func;
  68. return this;
  69. },
  70. /**
  71. * Callback function when remove a data
  72. */
  73. remove: function (func) {
  74. this._remove = func;
  75. return this;
  76. },
  77. execute: function () {
  78. var oldArr = this._old;
  79. var newArr = this._new;
  80. var oldDataIndexMap = {};
  81. var newDataIndexMap = {};
  82. var oldDataKeyArr = [];
  83. var newDataKeyArr = [];
  84. var i;
  85. initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter', this);
  86. initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter', this);
  87. for (i = 0; i < oldArr.length; i++) {
  88. var key = oldDataKeyArr[i];
  89. var idx = newDataIndexMap[key]; // idx can never be empty array here. see 'set null' logic below.
  90. if (idx != null) {
  91. // Consider there is duplicate key (for example, use dataItem.name as key).
  92. // We should make sure every item in newArr and oldArr can be visited.
  93. var len = idx.length;
  94. if (len) {
  95. len === 1 && (newDataIndexMap[key] = null);
  96. idx = idx.shift();
  97. } else {
  98. newDataIndexMap[key] = null;
  99. }
  100. this._update && this._update(idx, i);
  101. } else {
  102. this._remove && this._remove(i);
  103. }
  104. }
  105. for (var i = 0; i < newDataKeyArr.length; i++) {
  106. var key = newDataKeyArr[i];
  107. if (newDataIndexMap.hasOwnProperty(key)) {
  108. var idx = newDataIndexMap[key];
  109. if (idx == null) {
  110. continue;
  111. } // idx can never be empty array here. see 'set null' logic above.
  112. if (!idx.length) {
  113. this._add && this._add(idx);
  114. } else {
  115. for (var j = 0, len = idx.length; j < len; j++) {
  116. this._add && this._add(idx[j]);
  117. }
  118. }
  119. }
  120. }
  121. }
  122. };
  123. function initIndexMap(arr, map, keyArr, keyGetterName, dataDiffer) {
  124. for (var i = 0; i < arr.length; i++) {
  125. // Add prefix to avoid conflict with Object.prototype.
  126. var key = '_ec_' + dataDiffer[keyGetterName](arr[i], i);
  127. var existence = map[key];
  128. if (existence == null) {
  129. keyArr.push(key);
  130. map[key] = i;
  131. } else {
  132. if (!existence.length) {
  133. map[key] = existence = [existence];
  134. }
  135. existence.push(i);
  136. }
  137. }
  138. }
  139. var _default = DataDiffer;
  140. module.exports = _default;