DataDiffer.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. function defaultKeyGetter(item) {
  2. return item;
  3. }
  4. /**
  5. * @param {Array} oldArr
  6. * @param {Array} newArr
  7. * @param {Function} oldKeyGetter
  8. * @param {Function} newKeyGetter
  9. * @param {Object} [context] Can be visited by this.context in callback.
  10. */
  11. function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context) {
  12. this._old = oldArr;
  13. this._new = newArr;
  14. this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;
  15. this._newKeyGetter = newKeyGetter || defaultKeyGetter;
  16. this.context = context;
  17. }
  18. DataDiffer.prototype = {
  19. constructor: DataDiffer,
  20. /**
  21. * Callback function when add a data
  22. */
  23. add: function (func) {
  24. this._add = func;
  25. return this;
  26. },
  27. /**
  28. * Callback function when update a data
  29. */
  30. update: function (func) {
  31. this._update = func;
  32. return this;
  33. },
  34. /**
  35. * Callback function when remove a data
  36. */
  37. remove: function (func) {
  38. this._remove = func;
  39. return this;
  40. },
  41. execute: function () {
  42. var oldArr = this._old;
  43. var newArr = this._new;
  44. var oldDataIndexMap = {};
  45. var newDataIndexMap = {};
  46. var oldDataKeyArr = [];
  47. var newDataKeyArr = [];
  48. var i;
  49. initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter', this);
  50. initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter', this); // Travel by inverted order to make sure order consistency
  51. // when duplicate keys exists (consider newDataIndex.pop() below).
  52. // For performance consideration, these code below do not look neat.
  53. for (i = 0; i < oldArr.length; i++) {
  54. var key = oldDataKeyArr[i];
  55. var idx = newDataIndexMap[key]; // idx can never be empty array here. see 'set null' logic below.
  56. if (idx != null) {
  57. // Consider there is duplicate key (for example, use dataItem.name as key).
  58. // We should make sure every item in newArr and oldArr can be visited.
  59. var len = idx.length;
  60. if (len) {
  61. len === 1 && (newDataIndexMap[key] = null);
  62. idx = idx.unshift();
  63. } else {
  64. newDataIndexMap[key] = null;
  65. }
  66. this._update && this._update(idx, i);
  67. } else {
  68. this._remove && this._remove(i);
  69. }
  70. }
  71. for (var i = 0; i < newDataKeyArr.length; i++) {
  72. var key = newDataKeyArr[i];
  73. if (newDataIndexMap.hasOwnProperty(key)) {
  74. var idx = newDataIndexMap[key];
  75. if (idx == null) {
  76. continue;
  77. } // idx can never be empty array here. see 'set null' logic above.
  78. if (!idx.length) {
  79. this._add && this._add(idx);
  80. } else {
  81. for (var j = 0, len = idx.length; j < len; j++) {
  82. this._add && this._add(idx[j]);
  83. }
  84. }
  85. }
  86. }
  87. }
  88. };
  89. function initIndexMap(arr, map, keyArr, keyGetterName, dataDiffer) {
  90. for (var i = 0; i < arr.length; i++) {
  91. // Add prefix to avoid conflict with Object.prototype.
  92. var key = '_ec_' + dataDiffer[keyGetterName](arr[i], i);
  93. var existence = map[key];
  94. if (existence == null) {
  95. keyArr.push(key);
  96. map[key] = i;
  97. } else {
  98. if (!existence.length) {
  99. map[key] = existence = [existence];
  100. }
  101. existence.push(i);
  102. }
  103. }
  104. }
  105. var _default = DataDiffer;
  106. module.exports = _default;