forked from mystiq/hydrogen-web
fixes and tests for add, remove, move and update
This commit is contained in:
parent
290886a5eb
commit
2e362d1bbd
1 changed files with 124 additions and 15 deletions
|
@ -62,19 +62,21 @@ export default class SortedMapList extends BaseObservableList {
|
||||||
constructor(sourceMap, comparator) {
|
constructor(sourceMap, comparator) {
|
||||||
super();
|
super();
|
||||||
this._sourceMap = sourceMap;
|
this._sourceMap = sourceMap;
|
||||||
this._comparator = comparator;
|
this._comparator = (a, b) => comparator(a.value, b.value);
|
||||||
this._sortedPairs = null;
|
this._sortedPairs = null;
|
||||||
this._mapSubscription = null;
|
this._mapSubscription = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(key, value) {
|
onAdd(key, value) {
|
||||||
const idx = sortedIndex(this._sortedPairs, value, this._comparator);
|
const pair = {key, value};
|
||||||
this._sortedPairs.splice(idx, 0, {key, value});
|
const idx = sortedIndex(this._sortedPairs, pair, this._comparator);
|
||||||
|
this._sortedPairs.splice(idx, 0, pair);
|
||||||
this.emitAdd(idx, value);
|
this.emitAdd(idx, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemove(key, value) {
|
onRemove(key, value) {
|
||||||
const idx = sortedIndex(this._sortedPairs, value, this._comparator);
|
const pair = {key, value};
|
||||||
|
const idx = sortedIndex(this._sortedPairs, pair, this._comparator);
|
||||||
// assert key === this._sortedPairs[idx].key;
|
// assert key === this._sortedPairs[idx].key;
|
||||||
this._sortedPairs.splice(idx, 1);
|
this._sortedPairs.splice(idx, 1);
|
||||||
this.emitRemove(idx, value);
|
this.emitRemove(idx, value);
|
||||||
|
@ -83,11 +85,12 @@ export default class SortedMapList extends BaseObservableList {
|
||||||
onUpdate(key, value, params) {
|
onUpdate(key, value, params) {
|
||||||
// TODO: suboptimal for performance, see above for idea with BST to speed this up if we need to
|
// TODO: suboptimal for performance, see above for idea with BST to speed this up if we need to
|
||||||
const oldIdx = this._sortedPairs.findIndex(p => p.key === key);
|
const oldIdx = this._sortedPairs.findIndex(p => p.key === key);
|
||||||
// neccesary to remove item from array before
|
// neccesary to remove pair from array before
|
||||||
// doing sortedIndex as it relies on being sorted
|
// doing sortedIndex as it relies on being sorted
|
||||||
this._sortedPairs.splice(oldIdx, 1);
|
this._sortedPairs.splice(oldIdx, 1);
|
||||||
const newIdx = sortedIndex(this._sortedPairs, value, this._comparator);
|
const pair = {key, value};
|
||||||
this._sortedPairs.splice(newIdx, 0, {key, value});
|
const newIdx = sortedIndex(this._sortedPairs, pair, this._comparator);
|
||||||
|
this._sortedPairs.splice(newIdx, 0, pair);
|
||||||
if (oldIdx !== newIdx) {
|
if (oldIdx !== newIdx) {
|
||||||
this.emitMove(oldIdx, newIdx, value);
|
this.emitMove(oldIdx, newIdx, value);
|
||||||
}
|
}
|
||||||
|
@ -103,8 +106,8 @@ export default class SortedMapList extends BaseObservableList {
|
||||||
this._mapSubscription = this._sourceMap.subscribe(this);
|
this._mapSubscription = this._sourceMap.subscribe(this);
|
||||||
this._sortedPairs = new Array(this._sourceMap.size);
|
this._sortedPairs = new Array(this._sourceMap.size);
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (let [, value] of this._sourceMap) {
|
for (let [key, value] of this._sourceMap) {
|
||||||
this._sortedPairs[i] = value;
|
this._sortedPairs[i] = {key, value};
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
this._sortedPairs.sort(this._comparator);
|
this._sortedPairs.sort(this._comparator);
|
||||||
|
@ -118,7 +121,7 @@ export default class SortedMapList extends BaseObservableList {
|
||||||
}
|
}
|
||||||
|
|
||||||
get(index) {
|
get(index) {
|
||||||
return this._sortedPairs[index];
|
return this._sortedPairs[index].value;
|
||||||
}
|
}
|
||||||
|
|
||||||
get length() {
|
get length() {
|
||||||
|
@ -126,7 +129,16 @@ export default class SortedMapList extends BaseObservableList {
|
||||||
}
|
}
|
||||||
|
|
||||||
[Symbol.iterator]() {
|
[Symbol.iterator]() {
|
||||||
return this._sortedPairs.values();
|
const it = this._sortedPairs.values();
|
||||||
|
return {
|
||||||
|
next() {
|
||||||
|
const v = it.next();
|
||||||
|
if (v.value) {
|
||||||
|
v.value = v.value.value;
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,11 +148,15 @@ import ObservableMap from "../map/ObservableMap.js";
|
||||||
export function tests() {
|
export function tests() {
|
||||||
return {
|
return {
|
||||||
test_sortIndex(assert) {
|
test_sortIndex(assert) {
|
||||||
let idx = sortedIndex([1, 5, 6, 8], 0, (a, b) => a - b);
|
const a = [1, 5, 6, 8];
|
||||||
|
const cmp = (a, b) => a - b;
|
||||||
|
let idx = sortedIndex(a, 0, cmp);
|
||||||
assert.equal(idx, 0);
|
assert.equal(idx, 0);
|
||||||
idx = sortedIndex([1, 5, 6, 8], 3, (a, b) => a - b);
|
idx = sortedIndex(a, 3, cmp);
|
||||||
assert.equal(idx, 1);
|
assert.equal(idx, 1);
|
||||||
idx = sortedIndex([1, 5, 6, 8], 8, (a, b) => a - b);
|
idx = sortedIndex(a, 5, cmp);
|
||||||
|
assert.equal(idx, 1);
|
||||||
|
idx = sortedIndex(a, 8, cmp);
|
||||||
assert.equal(idx, 3);
|
assert.equal(idx, 3);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -167,7 +183,100 @@ export function tests() {
|
||||||
const list = new SortedMapList(map, (a, b) => a - b);
|
const list = new SortedMapList(map, (a, b) => a - b);
|
||||||
list.subscribe({}); //needed to populate iterator
|
list.subscribe({}); //needed to populate iterator
|
||||||
assert.deepEqual(Array.from(list), [-5, 6, 50]);
|
assert.deepEqual(Array.from(list), [-5, 6, 50]);
|
||||||
|
assert.equal(list.length, 3);
|
||||||
|
},
|
||||||
|
|
||||||
|
test_add(assert) {
|
||||||
|
const map = new ObservableMap([["a", 50], ["b", 6], ["c", -5]]);
|
||||||
|
const list = new SortedMapList(map, (a, b) => a - b);
|
||||||
|
let fired = 0;
|
||||||
|
list.subscribe({
|
||||||
|
onAdd(idx, value) {
|
||||||
|
fired++;
|
||||||
|
assert.equal(idx, 2);
|
||||||
|
assert.equal(value, 20);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
map.add("d", 20);
|
||||||
|
assert.equal(fired, 1);
|
||||||
|
assert.equal(list.length, 4);
|
||||||
|
},
|
||||||
|
|
||||||
|
test_remove(assert) {
|
||||||
|
const map = new ObservableMap([["a", 50], ["b", 6], ["c", -5]]);
|
||||||
|
const list = new SortedMapList(map, (a, b) => a - b);
|
||||||
|
let fired = 0;
|
||||||
|
list.subscribe({
|
||||||
|
onRemove(idx, value) {
|
||||||
|
fired++;
|
||||||
|
assert.equal(idx, 2);
|
||||||
|
assert.equal(value, 50);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
map.remove("a");
|
||||||
|
assert.equal(fired, 1);
|
||||||
|
assert.equal(list.length, 2);
|
||||||
|
},
|
||||||
|
|
||||||
|
test_move_reference(assert) {
|
||||||
|
const a = {number: 3};
|
||||||
|
const map = new ObservableMap([
|
||||||
|
["a", a],
|
||||||
|
["b", {number: 11}],
|
||||||
|
["c", {number: 1}],
|
||||||
|
]);
|
||||||
|
const list = new SortedMapList(map, (a, b) => a.number - b.number);
|
||||||
|
let updateFired = 0, moveFired = 0;
|
||||||
|
list.subscribe({
|
||||||
|
onUpdate(idx, value, param) {
|
||||||
|
updateFired++;
|
||||||
|
assert.equal(idx, 2);
|
||||||
|
assert.equal(value, a);
|
||||||
|
assert.equal(param, "number");
|
||||||
|
},
|
||||||
|
|
||||||
|
onMove(oldIdx, newIdx, value) {
|
||||||
|
moveFired++;
|
||||||
|
assert.equal(oldIdx, 1);
|
||||||
|
assert.equal(newIdx, 2);
|
||||||
|
assert.equal(value, a);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
a.number = 111;
|
||||||
|
map.update("a", "number");
|
||||||
|
assert.equal(moveFired, 1);
|
||||||
|
assert.equal(updateFired, 1);
|
||||||
|
},
|
||||||
|
|
||||||
|
test_update_without_move(assert) {
|
||||||
|
const a = {number: 3};
|
||||||
|
const map = new ObservableMap([
|
||||||
|
["a", a],
|
||||||
|
["b", {number: 11}],
|
||||||
|
["c", {number: 1}],
|
||||||
|
]);
|
||||||
|
const list = new SortedMapList(map, (a, b) => a.number - b.number);
|
||||||
|
let updateFired = 0, moveFired = 0;
|
||||||
|
list.subscribe({
|
||||||
|
onUpdate(idx, value, param) {
|
||||||
|
updateFired++;
|
||||||
|
assert.equal(idx, 1);
|
||||||
|
assert.equal(value, a);
|
||||||
|
assert.equal(param, "number");
|
||||||
|
},
|
||||||
|
|
||||||
|
onMove() {
|
||||||
|
moveFired++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assert.deepEqual(Array.from(list).map(v => v.number), [1, 3, 11]);
|
||||||
|
// asume some part of a that doesn't affect
|
||||||
|
// sorting order has changed here
|
||||||
|
map.update("a", "number");
|
||||||
|
assert.equal(moveFired, 0);
|
||||||
|
assert.equal(updateFired, 1);
|
||||||
|
assert.deepEqual(Array.from(list).map(v => v.number), [1, 3, 11]);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//#endif
|
//#endif
|
||||||
|
|
Loading…
Reference in a new issue