161 lines
4.5 KiB
JavaScript
161 lines
4.5 KiB
JavaScript
|
|
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
/*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
function defaultKeyGetter(item) {
|
|
return item;
|
|
}
|
|
/**
|
|
* @param {Array} oldArr
|
|
* @param {Array} newArr
|
|
* @param {Function} oldKeyGetter
|
|
* @param {Function} newKeyGetter
|
|
* @param {Object} [context] Can be visited by this.context in callback.
|
|
*/
|
|
|
|
|
|
function DataDiffer(oldArr, newArr, oldKeyGetter, newKeyGetter, context) {
|
|
this._old = oldArr;
|
|
this._new = newArr;
|
|
this._oldKeyGetter = oldKeyGetter || defaultKeyGetter;
|
|
this._newKeyGetter = newKeyGetter || defaultKeyGetter;
|
|
this.context = context;
|
|
}
|
|
|
|
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 oldDataIndexMap = {};
|
|
var newDataIndexMap = {};
|
|
var oldDataKeyArr = [];
|
|
var newDataKeyArr = [];
|
|
var i;
|
|
initIndexMap(oldArr, oldDataIndexMap, oldDataKeyArr, '_oldKeyGetter', this);
|
|
initIndexMap(newArr, newDataIndexMap, newDataKeyArr, '_newKeyGetter', this);
|
|
|
|
for (i = 0; i < oldArr.length; i++) {
|
|
var key = oldDataKeyArr[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.shift();
|
|
} else {
|
|
newDataIndexMap[key] = null;
|
|
}
|
|
|
|
this._update && this._update(idx, i);
|
|
} else {
|
|
this._remove && this._remove(i);
|
|
}
|
|
}
|
|
|
|
for (var i = 0; i < newDataKeyArr.length; i++) {
|
|
var key = newDataKeyArr[i];
|
|
|
|
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 j = 0, len = idx.length; j < len; j++) {
|
|
this._add && this._add(idx[j]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
function initIndexMap(arr, map, keyArr, keyGetterName, dataDiffer) {
|
|
for (var i = 0; i < arr.length; i++) {
|
|
// Add prefix to avoid conflict with Object.prototype.
|
|
var key = '_ec_' + dataDiffer[keyGetterName](arr[i], i);
|
|
var existence = map[key];
|
|
|
|
if (existence == null) {
|
|
keyArr.push(key);
|
|
map[key] = i;
|
|
} else {
|
|
if (!existence.length) {
|
|
map[key] = existence = [existence];
|
|
}
|
|
|
|
existence.push(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
var _default = DataDiffer;
|
|
module.exports = _default; |