Commit e3776108 by Ivan Kubota

Store:

Arrays can have indexes. ArrayStore instance .index({'ID': true, 'reverseID': true}) DOM Export as node.js module (for backend usage. great with simplest-dom)
parent aa37a0fc
...@@ -833,3 +833,6 @@ var D = NS.D, ...@@ -833,3 +833,6 @@ var D = NS.D,
page: {}, page: {},
cmp: {} cmp: {}
}; };
if(typeof module !== 'undefined')
module.exports = D;
\ No newline at end of file
...@@ -5,7 +5,7 @@ License: ...@@ -5,7 +5,7 @@ License:
MPL-2.0 MPL-2.0
Developed in: Developed in:
2012-2020 2012-2023
Mozilla Public License, version 2.0 Mozilla Public License, version 2.0
......
...@@ -735,6 +735,7 @@ const ArrayStore = function(cfg) { ...@@ -735,6 +735,7 @@ const ArrayStore = function(cfg) {
}; };
ArrayStore.prototype = { ArrayStore.prototype = {
_index: false,
_exposeGet: function() { _exposeGet: function() {
if( !('get' in this._props) ){ if( !('get' in this._props) ){
Object.defineProperties( this._props, { Object.defineProperties( this._props, {
...@@ -759,11 +760,13 @@ ArrayStore.prototype = { ...@@ -759,11 +760,13 @@ ArrayStore.prototype = {
return this._props; return this._props;
}, },
_fireAdd: function (item, pos) { _fireAdd: function (item, pos) {
this._index && this._addToIndex(item);
var arr = this._props; var arr = this._props;
this.fire('add', item, pos > 0 ? arr.get(pos-1) : null, pos < this.length - 1 ? arr.get(pos+1) : null, pos) this.fire('add', item, pos > 0 ? arr.get(pos-1) : null, pos < this.length - 1 ? arr.get(pos+1) : null, pos)
this._notifyBubble([pos], '', true); this._notifyBubble([pos], '', true);
}, },
_fireRemove: function (item, pos) { _fireRemove: function (item, pos) {
this._index && this._removeFromIndex(item);
var arr = this._props; var arr = this._props;
this.fire('remove', item, pos > 0 ? arr.get(pos-1) : null, pos < this.length ? arr.get(pos) : null, pos) this.fire('remove', item, pos > 0 ? arr.get(pos-1) : null, pos < this.length ? arr.get(pos) : null, pos)
this._notifyBubble([], '', true); this._notifyBubble([], '', true);
...@@ -852,6 +855,7 @@ ArrayStore.prototype = { ...@@ -852,6 +855,7 @@ ArrayStore.prototype = {
return item; return item;
}, },
insert: function(item, pos){ insert: function(item, pos){
this._props.splice(pos, 0, item); this._props.splice(pos, 0, item);
this.length++; this.length++;
this._fireAdd(item, pos) this._fireAdd(item, pos)
...@@ -865,6 +869,106 @@ ArrayStore.prototype = { ...@@ -865,6 +869,106 @@ ArrayStore.prototype = {
filter: function(fn) { filter: function(fn) {
return this._props.filter(fn); return this._props.filter(fn);
}, },
index: function(cfg){
this._index = true;
this._indexes = this._indexes || {};
var _self = this;
for(var key in cfg){
this._indexes[key] = {cfg: cfg, data: {}};
}
this._props.forEach(function(item){
_self._addToIndex(item, true);
});
return this;
},
_addToIndex: function(item, suppressErrors){
var _indexes = this._indexes
for(var key in _indexes){
var data = _indexes[key].data;
(data[item[key]] || (data[item[key]] = [])).push(item);
}
},
_removeFromIndex: function(item){
for(var key in this._indexes){
var index = this._indexes[key];
var keyValue = item[key];
var arr = index.data[keyValue];
for(var i = arr.length; i >= 0; i--){
if(arr[i] === item){
arr.splice(i, 1);
}
}
if(arr.length === 0){
delete index.data[keyValue];
}
}
},
find: function(cfg){
var dataToFilter;
dataToFilter = this._props;
if(!this._index){
noUnindexed = false;
}else{
var indexed = [], unindexed = {}, noUnindexed = true;
for(var key in cfg){
if(key in this._indexes){
indexed.push({key: key, val: cfg[key]});
}else{
unindexed[key] = cfg[key];
noUnindexed = false
}
}
cfg = unindexed;
for(var i = 0, _i = indexed.length; i < _i; i++){
indexed[i] = indexed[i].key in this._indexes ? this._indexes[indexed[i].key].data[indexed[i].val] : []
}
if(indexed.length) {
if(indexed.length === 1){
dataToFilter = indexed[0]
}else{
// reduce intersection of matched indexes
indexed.sort(function(a,b){
return a.length - b.length;
});
var a = new Set(indexed[0]);
var b = new Set(), tmp;
for(var i = 1, _i = indexed.length; i < _i; i++){
var other = indexed[i];
b.clear();
for(var j = 1, _j = other.length; j < _j; j++){
var item = other[j];
a.has(item) && b.add(item);
}
tmp = a;
a = b;
b = tmp;
}
dataToFilter = Array.from(a);
}
}
}
// SLOW STRAIGHTFORWARD IMPLEMENTATION
return noUnindexed ? dataToFilter : dataToFilter.filter(function(obj){
var suit = true;
for(var key in cfg){
if(typeof cfg[key] !== 'function') {
suit = suit && obj[ key ] === cfg[ key ];
}else {
suit = suit && cfg[ key ].call( obj, obj[ key ] )
}
if(!suit) {
return false;
}
}
return suit;
});
},
items: function() { items: function() {
//return this.item() //return this.item()
}, },
...@@ -878,6 +982,8 @@ ArrayStore.prototype = { ...@@ -878,6 +982,8 @@ ArrayStore.prototype = {
} }
}; };
ArrayStore.prototype = Object.assign(new Store(), ArrayStore.prototype); ArrayStore.prototype = Object.assign(new Store(), ArrayStore.prototype);
ArrayStore.UNIQUE = 'UNIQUE';
Store.ArrayStore = ArrayStore; Store.ArrayStore = ArrayStore;
// Object.assign does not work with nested prototypes // Object.assign does not work with nested prototypes
......
{ {
"name": "react-vanilla", "name": "react-vanilla",
"version": "1.1.18", "version": "1.1.19",
"description": "", "description": "",
"main": "DOM.js", "main": "DOM.js",
"scripts": { "scripts": {
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
"url": "git@gitlab.quokka.pub:Zibx/react-vanilla.git" "url": "git@gitlab.quokka.pub:Zibx/react-vanilla.git"
}, },
"keywords": [], "keywords": [],
"author": "", "author": "Zibx",
"license": "MPL-2.0", "license": "MPL-2.0",
"dependencies": { "dependencies": {
"simplest-dom": "^0.0.20", "simplest-dom": "^0.0.20",
......
...@@ -358,6 +358,99 @@ describe('array events', function() { ...@@ -358,6 +358,99 @@ describe('array events', function() {
}); });
}); });
describe('Array store should be indexable', function() {
it('indexes should make it faster', function(){
var testCount = 1200;
var accessSequence = [];
for(var i = 0; i < testCount; i++)
accessSequence.push(Math.random()*testCount|0);
var bench = function(s, count){
var tStart = +new Date();
for(var i = 0; i < count; i++){
s.push({ID: i+'-id', num: i, reverseID: count-i-1+'-rev'});
}
var tFilled = +new Date();
for(var i = 0, _i = accessSequence.length; i < _i; i++){
var id = accessSequence[i];
assert.equal(s.find({ID: id+'-id'})[0].num, id);
}
for(var i = 0, _i = accessSequence.length; i < _i; i++){
var id = accessSequence[i];
assert.equal(s.find({reverseID: id+'-rev'})[0].num, count-id-1);
}
var tEnd = +new Date();
return {fill: tFilled-tStart, search: tEnd-tFilled, total: tEnd - tStart}
};
var getMetrics = function(dataPrepareFn) {
var result = [];
for(var i = 0; i <10; i++){
result.push(dataPrepareFn(bench));
}
var sorted = result.sort((a,b)=>a.total-b.total);
sorted.pop();
sorted.shift();
var reduced = sorted.reduce(function(store, obj){
for(var key in obj){
store[key] = (store[key]||0)+obj[key];
}
return store;
},{});
for(var key in reduced){
reduced[key] /= sorted.length;
}
return reduced;
};
var original = getMetrics(function(bench){
var s = new Store({arr: [], indexedArr: []});
var notIndexed = s.array('arr');
return bench(notIndexed, testCount)
});
/*
var indexedID = getMetrics(function(bench){
var s = new Store({arr: [], indexedArr: []});
var indexed = s.array('arr').index({'ID': true});
return bench(indexed, testCount)
});*/
var indexed = getMetrics(function(bench){
var s = new Store({arr: [], indexedArr: []});
var indexed = s.array('arr').index({'ID': true, 'reverseID': true});
return bench(indexed, testCount)
});
assert.equal(original.fill/1.1 < indexed.fill, true)
assert.equal(original.search / Math.log2(testCount)/2> indexed.search, true)
console.log(original);
console.log(indexed);
});
it('should work with multiple indexes', function(){
var s = new Store({arr: []}),
arr = s.array('arr');
for(var i = 'a'; i <= 'm'; i = String.fromCharCode(i.charCodeAt(0)+1)) {
arr.index({[i]: true});
}
var start = +new Date();
for(var i = 1; i < 100000; i++){
arr.push({a: i, b: i%2, c:i%3, d: i%5, e: i%7, f: i%11, g: i%13, h: i%17, i: i%19, j: i%23})
}
for(var i = 1; i < 100; i++){
arr.pop()
}
var matchStart = +new Date();
for(var i = 1; i < 1000; i++) {
assert( Math.abs( 100000 / ( 19 * 23 ) - ( arr.find( { i: 0, j: 0 } ).length ) ) < 3 );
}
var end = +new Date();
console.log(matchStart-start, end-matchStart);
});
});
describe('Bug with subscribing to multiple bindings', function() { describe('Bug with subscribing to multiple bindings', function() {
it('should call fn with both values setted', function(){ it('should call fn with both values setted', function(){
const s = new Store( { a: '1', b: '2' } ), const s = new Store( { a: '1', b: '2' } ),
...@@ -430,5 +523,6 @@ describe('Feature. Binding.valEqual', function() { ...@@ -430,5 +523,6 @@ describe('Feature. Binding.valEqual', function() {
assert.deepEqual(changes, [true, false, true, false]); assert.deepEqual(changes, [true, false, true, false]);
}) });
}); });
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment