mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2024-12-23 11:35:04 +01:00
work on sorted list from map
This commit is contained in:
parent
5bff41c1ee
commit
1441abbf7e
30
src/observable/BaseObservableCollection.js
Normal file
30
src/observable/BaseObservableCollection.js
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
export default class BaseObservableCollection {
|
||||||
|
constructor() {
|
||||||
|
this._handlers = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
onSubscribeFirst() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnsubscribeLast() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe(handler) {
|
||||||
|
this._handlers.add(handler);
|
||||||
|
if (this._handlers.length === 1) {
|
||||||
|
this.onSubscribeFirst();
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
if (handler) {
|
||||||
|
this._handlers.delete(this._handler);
|
||||||
|
if (this._handlers.length === 0) {
|
||||||
|
this.onUnsubscribeLast();
|
||||||
|
}
|
||||||
|
handler = null;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
37
src/observable/list/BaseObservableList.js
Normal file
37
src/observable/list/BaseObservableList.js
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import BaseObservableCollection from "../BaseObservableCollection.js";
|
||||||
|
|
||||||
|
export default class BaseObservableList extends BaseObservableCollection {
|
||||||
|
emitReset() {
|
||||||
|
for(let h of this._handlers) {
|
||||||
|
h.onReset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// we need batch events, mostly on index based collection though?
|
||||||
|
// maybe we should get started without?
|
||||||
|
emitAdd(index, value) {
|
||||||
|
for(let h of this._handlers) {
|
||||||
|
h.onAdd(index, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emitChange(index, value, params) {
|
||||||
|
for(let h of this._handlers) {
|
||||||
|
h.onChange(index, value, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
emitRemove(index, value) {
|
||||||
|
for(let h of this._handlers) {
|
||||||
|
h.onRemove(index, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// toIdx assumes the item has already
|
||||||
|
// been removed from its fromIdx
|
||||||
|
emitMove(fromIdx, toIdx, value) {
|
||||||
|
for(let h of this._handlers) {
|
||||||
|
h.onMove(fromIdx, toIdx, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,30 @@
|
|||||||
import Operator from "../Operator.js";
|
import BaseObservableList from "../BaseObservableList.js";
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
when a value changes, it sorting order can change. It would still be at the old index prior to firing an onChange event.
|
||||||
|
So how do you know where it was before it changed, if not by going over all values?
|
||||||
|
|
||||||
|
how to make this fast?
|
||||||
|
|
||||||
|
seems hard to solve with an array, because you need to map the key to it's previous location somehow, to efficiently find it,
|
||||||
|
and move it.
|
||||||
|
|
||||||
|
I wonder if we could do better with a binary search tree (BST).
|
||||||
|
The tree has a value with {key, value}. There is a plain Map mapping keys to this tuple,
|
||||||
|
for easy lookup. Now how do we find the index of this tuple in the BST?
|
||||||
|
|
||||||
|
either we store in every node the amount of nodes on the left and right, or we decend into the part
|
||||||
|
of the tree preceding the node we want to know about. Updating the counts upwards would probably be fine as this is log2 of
|
||||||
|
the size of the container.
|
||||||
|
|
||||||
|
to be able to go from a key to an index, the value would have the have a link with the tree node though
|
||||||
|
|
||||||
|
so key -> Map<key,value> -> value -> node -> *parentNode -> rootNode
|
||||||
|
with a node containing {value, leftCount, rightCount, leftNode, rightNode, parentNode}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @license
|
* @license
|
||||||
@ -26,44 +52,54 @@ function sortedIndex(array, value, comparator) {
|
|||||||
}
|
}
|
||||||
return high;
|
return high;
|
||||||
}
|
}
|
||||||
|
// no duplicates allowed for now
|
||||||
// TODO: this should not inherit from an BaseObservableMap, as it's a list
|
export default class SortOperator extends BaseObservableList {
|
||||||
export default class SortOperator extends Operator {
|
constructor(sourceMap, comparator, getKey) {
|
||||||
constructor(sourceMap, comparator) {
|
super();
|
||||||
super(sourceMap);
|
this._sourceMap = sourceMap;
|
||||||
this._comparator = comparator;
|
this._comparator = comparator;
|
||||||
|
this._getKey = getKey;
|
||||||
this._sortedValues = [];
|
this._sortedValues = [];
|
||||||
this._keyIndex = new Map();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onAdd(key, value) {
|
onAdd(key, value) {
|
||||||
const idx = sortedIndex(this._sortedValues, value, this._comparator);
|
const idx = sortedIndex(this._sortedValues, value, this._comparator);
|
||||||
this._sortedValues.splice(idx, 0, value);
|
this._sortedValues.splice(idx, 0, value);
|
||||||
this._keyIndex.set(key, idx);
|
|
||||||
this.emitAdd(idx, value);
|
this.emitAdd(idx, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
onRemove(key, _value) {
|
onRemove(key, value) {
|
||||||
const idx = sortedIndex(this._sortedValues, value, this._comparator);
|
const idx = sortedIndex(this._sortedValues, value, this._comparator);
|
||||||
this._sortedValues.splice(idx, 0, value);
|
this._sortedValues.splice(idx, 1);
|
||||||
this._keyIndex.set(key, idx);
|
this.emitRemove(idx, value);
|
||||||
this.emitAdd(idx, value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onChange(key, value, params) {
|
onChange(key, value, params) {
|
||||||
// index could have moved if other items got added in the meantime
|
const idxIfSortUnchanged = sortedIndex(this._sortedValues, value, this._comparator);
|
||||||
const oldIdx = this._keyIndex.get(key);
|
if (this._getKey(this._sortedValues[idxIfSortUnchanged]) === key) {
|
||||||
this._sortedValues.splice(oldIdx, 1);
|
this.emitChange(idxIfSortUnchanged, value, params);
|
||||||
const idx = sortedIndex(this._sortedValues, value, this._comparator);
|
} else {
|
||||||
|
// TODO: slow!? See above for idea with BST to speed this up if we need to
|
||||||
|
const oldIdx = this._sortedValues.find(v => this._getKey(v) === key);
|
||||||
|
this._sortedValues.splice(oldIdx, 1);
|
||||||
|
const newIdx = sortedIndex(this._sortedValues, value, this._comparator);
|
||||||
|
this._sortedValues.splice(newIdx, 0, value);
|
||||||
|
this.emitMove(oldIdx, newIdx, value);
|
||||||
|
this.emitChange(newIdx, value, params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onReset() {
|
||||||
|
this._sortedValues = [];
|
||||||
|
this.emitReset();
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubscribeFirst() {
|
onSubscribeFirst() {
|
||||||
this._sortedValues = new Array(this._source.size);
|
this._mapSubscription = this._sourceMap.subscribe(this);
|
||||||
|
this._sortedValues = new Array(this._sourceMap.size);
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (let [key, value] of this._source) {
|
for (let [, value] of this._sourceMap) {
|
||||||
this._sortedValues[i] = value;
|
this._sortedValues[i] = value;
|
||||||
this._keyIndex.set(key, i);
|
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
this._sortedValues.sort(this._comparator);
|
this._sortedValues.sort(this._comparator);
|
||||||
@ -73,15 +109,12 @@ export default class SortOperator extends Operator {
|
|||||||
onUnsubscribeLast() {
|
onUnsubscribeLast() {
|
||||||
super.onUnsubscribeLast();
|
super.onUnsubscribeLast();
|
||||||
this._sortedValues = null;
|
this._sortedValues = null;
|
||||||
|
this._mapSubscription = this._mapSubscription();
|
||||||
}
|
}
|
||||||
|
|
||||||
onReset() {
|
|
||||||
this._sortedValues = [];
|
|
||||||
this.emitReset();
|
|
||||||
}
|
|
||||||
|
|
||||||
get length() {
|
get length() {
|
||||||
return this._source.size;
|
return this._sourceMap.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Symbol.iterator]() {
|
[Symbol.iterator]() {
|
@ -1,11 +1,8 @@
|
|||||||
import MapOperator from "./operators/MapOperator.js";
|
import MapOperator from "./operators/MapOperator.js";
|
||||||
import SortOperator from "./operators/SortOperator.js";
|
import SortOperator from "./operators/SortOperator.js";
|
||||||
|
import BaseObservableCollection from "../BaseObservableCollection.js";
|
||||||
|
|
||||||
export default class BaseObservableMap {
|
export default class BaseObservableMap extends BaseObservableCollection {
|
||||||
constructor() {
|
|
||||||
this._handlers = new Set();
|
|
||||||
}
|
|
||||||
|
|
||||||
emitReset() {
|
emitReset() {
|
||||||
for(let h of this._handlers) {
|
for(let h of this._handlers) {
|
||||||
h.onReset();
|
h.onReset();
|
||||||
@ -31,31 +28,6 @@ export default class BaseObservableMap {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onSubscribeFirst() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
onUnsubscribeLast() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
subscribe(handler) {
|
|
||||||
this._handlers.add(handler);
|
|
||||||
if (this._handlers.length === 1) {
|
|
||||||
this.onSubscribeFirst();
|
|
||||||
}
|
|
||||||
return () => {
|
|
||||||
if (handler) {
|
|
||||||
this._handlers.delete(this._handler);
|
|
||||||
if (this._handlers.length === 0) {
|
|
||||||
this.onUnsubscribeLast();
|
|
||||||
}
|
|
||||||
handler = null;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
map(mapper, updater) {
|
map(mapper, updater) {
|
||||||
return new MapOperator(this, mapper, updater);
|
return new MapOperator(this, mapper, updater);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user