fixed-data-table-one.com
Version:
A React table component designed to allow presenting thousands of rows of data.
185 lines (167 loc) • 7.4 kB
JavaScript
/**
* Copyright Schrodinger, LLC
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule IntegerBufferSet
* @typechecks
*/
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _Heap = require('./Heap');
var _Heap2 = _interopRequireDefault(_Heap);
var _invariant = require('./invariant');
var _invariant2 = _interopRequireDefault(_invariant);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
// Data structure that allows to store values and assign positions to them
// in a way to minimize changing positions of stored values when new ones are
// added or when some values are replaced. Stored elements are alwasy assigned
// a consecutive set of positoins startin from 0 up to count of elements less 1
// Following actions can be executed
// * get position assigned to given value (null if value is not stored)
// * create new entry for new value and get assigned position back
// * replace value that is furthest from specified value range with new value
// and get it's position back
// All operations take amortized log(n) time where n is number of elements in
// the set.
var IntegerBufferSet = function () {
function IntegerBufferSet() {
_classCallCheck(this, IntegerBufferSet);
this._valueToPositionMap = {};
this._size = 0;
this._smallValues = new _Heap2.default([], // Initial data in the heap
this._smallerComparator);
this._largeValues = new _Heap2.default([], // Initial data in the heap
this._greaterComparator);
this.getNewPositionForValue = this.getNewPositionForValue.bind(this);
this.getValuePosition = this.getValuePosition.bind(this);
this.getSize = this.getSize.bind(this);
this.replaceFurthestValuePosition = this.replaceFurthestValuePosition.bind(this);
}
_createClass(IntegerBufferSet, [{
key: 'getSize',
value: function getSize() /*number*/{
return this._size;
}
}, {
key: 'getValuePosition',
value: function getValuePosition( /*number*/value) /*?number*/{
if (this._valueToPositionMap[value] === undefined) {
return null;
}
return this._valueToPositionMap[value];
}
}, {
key: 'getNewPositionForValue',
value: function getNewPositionForValue( /*number*/value) /*number*/{
(0, _invariant2.default)(this._valueToPositionMap[value] === undefined, "Shouldn't try to find new position for value already stored in BufferSet");
var newPosition = this._size;
this._size++;
this._pushToHeaps(newPosition, value);
this._valueToPositionMap[value] = newPosition;
return newPosition;
}
}, {
key: 'replaceFurthestValuePosition',
value: function replaceFurthestValuePosition(
/*number*/lowValue,
/*number*/highValue,
/*number*/newValue) /*?number*/{
(0, _invariant2.default)(this._valueToPositionMap[newValue] === undefined, "Shouldn't try to replace values with value already stored value in " + "BufferSet");
this._cleanHeaps();
if (this._smallValues.empty() || this._largeValues.empty()) {
// Threre are currently no values stored. We will have to create new
// position for this value.
return null;
}
var minValue = this._smallValues.peek().value;
var maxValue = this._largeValues.peek().value;
if (minValue >= lowValue && maxValue <= highValue) {
// All values currently stored are necessary, we can't reuse any of them.
return null;
}
var valueToReplace;
if (lowValue - minValue > maxValue - highValue) {
// minValue is further from provided range. We will reuse it's position.
valueToReplace = minValue;
this._smallValues.pop();
} else {
valueToReplace = maxValue;
this._largeValues.pop();
}
var position = this._valueToPositionMap[valueToReplace];
delete this._valueToPositionMap[valueToReplace];
this._valueToPositionMap[newValue] = position;
this._pushToHeaps(position, newValue);
return position;
}
}, {
key: '_pushToHeaps',
value: function _pushToHeaps( /*number*/position, /*number*/value) {
var element = {
position: position,
value: value
};
// We can reuse the same object in both heaps, because we don't mutate them
this._smallValues.push(element);
this._largeValues.push(element);
}
}, {
key: '_cleanHeaps',
value: function _cleanHeaps() {
// We not usually only remove object from one heap while moving value.
// Here we make sure that there is no stale data on top of heaps.
this._cleanHeap(this._smallValues);
this._cleanHeap(this._largeValues);
var minHeapSize = Math.min(this._smallValues.size(), this._largeValues.size());
var maxHeapSize = Math.max(this._smallValues.size(), this._largeValues.size());
if (maxHeapSize > 10 * minHeapSize) {
// There are many old values in one of heaps. We nned to get rid of them
// to not use too avoid memory leaks
this._recreateHeaps();
}
}
}, {
key: '_recreateHeaps',
value: function _recreateHeaps() {
var sourceHeap = this._smallValues.size() < this._largeValues.size() ? this._smallValues : this._largeValues;
var newSmallValues = new _Heap2.default([], // Initial data in the heap
this._smallerComparator);
var newLargeValues = new _Heap2.default([], // Initial datat in the heap
this._greaterComparator);
while (!sourceHeap.empty()) {
var element = sourceHeap.pop();
// Push all stil valid elements to new heaps
if (this._valueToPositionMap[element.value] !== undefined) {
newSmallValues.push(element);
newLargeValues.push(element);
}
}
this._smallValues = newSmallValues;
this._largeValues = newLargeValues;
}
}, {
key: '_cleanHeap',
value: function _cleanHeap( /*object*/heap) {
while (!heap.empty() && this._valueToPositionMap[heap.peek().value] === undefined) {
heap.pop();
}
}
}, {
key: '_smallerComparator',
value: function _smallerComparator( /*object*/lhs, /*object*/rhs) /*boolean*/{
return lhs.value < rhs.value;
}
}, {
key: '_greaterComparator',
value: function _greaterComparator( /*object*/lhs, /*object*/rhs) /*boolean*/{
return lhs.value > rhs.value;
}
}]);
return IntegerBufferSet;
}();
module.exports = IntegerBufferSet;