120 lines
3.7 KiB
Plaintext
120 lines
3.7 KiB
Plaintext
define(function(require) {
|
|
'use strict';
|
|
|
|
function defaultKeyGetter(item) {
|
|
return item;
|
|
}
|
|
|
|
function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter) {
|
|
this._old = oldArr;
|
|
this._new = newArr;
|
|
|
|
this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;
|
|
this._newKeyGetter = newKeyGetter || defaultKeyGetter;
|
|
}
|
|
|
|
DataDiffer.prototype = {
|
|
|
|
constructor: DataDiffer,
|
|
|
|
/**
|
|
* Callback function when add a data
|
|
*/
|
|
add: function (func) {
|
|
this._add = func;
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Callback function when update a data
|
|
*/
|
|
update: function (func) {
|
|
this._update = func;
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Callback function when remove a data
|
|
*/
|
|
remove: function (func) {
|
|
this._remove = func;
|
|
return this;
|
|
},
|
|
|
|
execute: function () {
|
|
var oldArr = this._old;
|
|
var newArr = this._new;
|
|
var oldKeyGetter = this._oldKeyGetter;
|
|
var newKeyGetter = this._newKeyGetter;
|
|
|
|
var oldDataIndexMap = {};
|
|
var newDataIndexMap = {};
|
|
var i;
|
|
|
|
initIndexMap(oldArr, oldDataIndexMap, oldKeyGetter);
|
|
initIndexMap(newArr, newDataIndexMap, newKeyGetter);
|
|
|
|
// Travel by inverted order to make sure order consistency
|
|
// when duplicate keys exists (consider newDataIndex.pop() below).
|
|
// For performance consideration, these code below do not look neat.
|
|
for (i = 0; i < oldArr.length; i++) {
|
|
var key = oldKeyGetter(oldArr[i]);
|
|
var idx = newDataIndexMap[key];
|
|
|
|
// idx can never be empty array here. see 'set null' logic below.
|
|
if (idx != null) {
|
|
// Consider there is duplicate key (for example, use dataItem.name as key).
|
|
// We should make sure every item in newArr and oldArr can be visited.
|
|
var len = idx.length;
|
|
if (len) {
|
|
len === 1 && (newDataIndexMap[key] = null);
|
|
idx = idx.unshift();
|
|
}
|
|
else {
|
|
newDataIndexMap[key] = null;
|
|
}
|
|
this._update && this._update(idx, i);
|
|
}
|
|
else {
|
|
this._remove && this._remove(i);
|
|
}
|
|
}
|
|
|
|
for (var key in newDataIndexMap) {
|
|
if (newDataIndexMap.hasOwnProperty(key)) {
|
|
var idx = newDataIndexMap[key];
|
|
if (idx == null) {
|
|
continue;
|
|
}
|
|
// idx can never be empty array here. see 'set null' logic above.
|
|
if (!idx.length) {
|
|
this._add && this._add(idx);
|
|
}
|
|
else {
|
|
for (var i = 0, len = idx.length; i < len; i++) {
|
|
this._add && this._add(idx[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
function initIndexMap(arr, map, keyGetter) {
|
|
for (var i = 0; i < arr.length; i++) {
|
|
var key = keyGetter(arr[i]);
|
|
var existence = map[key];
|
|
if (existence == null) {
|
|
map[key] = i;
|
|
}
|
|
else {
|
|
if (!existence.length) {
|
|
map[key] = existence = [existence];
|
|
}
|
|
existence.push(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
return DataDiffer;
|
|
}); |