actually implemented filtered map

This commit is contained in:
Bruno Windels 2020-10-05 18:18:44 +02:00
parent 5ae1be9a9c
commit 943467cf67

View file

@ -17,56 +17,127 @@ limitations under the License.
import {BaseObservableMap} from "./BaseObservableMap.js";
export class FilteredMap extends BaseObservableMap {
constructor(source, mapper, updater) {
constructor(source, filter) {
super();
this._source = source;
this._mapper = mapper;
this._updater = updater;
this._mappedValues = new Map();
this._filter = filter;
/** @type {Map<string, bool>} */
this._included = null;
}
setFilter(filter) {
this._filter = filter;
this.update();
}
update() {
// TODO: need to check if we have a subscriber already? If not, we really should not iterate the source?
if (this._filter) {
this._included = this._included || new Map();
for (const [key, value] of this._source) {
this._included.set(key, this._filter(value, key));
}
} else {
this._included = null;
}
}
onAdd(key, value) {
const mappedValue = this._mapper(value);
this._mappedValues.set(key, mappedValue);
this.emitAdd(key, mappedValue);
if (this._filter) {
const included = this._filter(value, key);
this._included.set(key, included);
if (!included) {
return;
}
}
this.emitAdd(key, value);
}
onRemove(key, _value) {
const mappedValue = this._mappedValues.get(key);
if (this._mappedValues.delete(key)) {
this.emitRemove(key, mappedValue);
onRemove(key, value) {
if (this._filter && !this._included.get(key)) {
return;
}
this.emitRemove(key, value);
}
onChange(key, value, params) {
const mappedValue = this._mappedValues.get(key);
if (mappedValue !== undefined) {
const newParams = this._updater(value, params);
if (newParams !== undefined) {
this.emitChange(key, mappedValue, newParams);
}
if (this._filter) {
const wasIncluded = this._included.get(key);
const isIncluded = this._filter(value, key);
this._included.set(key, isIncluded);
if (wasIncluded && !isIncluded) {
this.emitRemove(key, value);
} else if (!wasIncluded && isIncluded) {
this.emitAdd(key, value);
} else if (!wasIncluded && !isIncluded) {
return;
} // fall through to emitChange
}
this.emitChange(key, value, params);
}
onSubscribeFirst() {
for (let [key, value] of this._source) {
const mappedValue = this._mapper(value);
this._mappedValues.set(key, mappedValue);
}
this.update();
super.onSubscribeFirst();
}
onUnsubscribeLast() {
super.onUnsubscribeLast();
this._mappedValues.clear();
this._included = null;
}
onReset() {
this._mappedValues.clear();
this.update();
this.emitReset();
}
[Symbol.iterator]() {
return this._mappedValues.entries()[Symbol.iterator];
return new FilterIterator(this._source, this._included);
}
}
class FilterIterator {
constructor(map, _included) {
this._included = _included;
this._sourceIterator = map.entries();
}
next() {
// eslint-disable-next-line no-constant-condition
while (true) {
const sourceResult = this._sourceIterator.next();
if (sourceResult.done) {
return sourceResult;
}
const key = sourceResult.value[1];
if (this._included.get(key)) {
return sourceResult;
}
}
}
}
import {ObservableMap} from "./ObservableMap.js";
export function tests() {
return {
"filter preloaded list": assert => {
const source = new ObservableMap();
source.add("one", 1);
source.add("two", 2);
source.add("three", 3);
const odds = Array.from(new FilteredMap(source, x => x % 2 !== 0));
assert.equal(odds.length, 2);
},
"filter added values": assert => {
},
"filter removed values": assert => {
},
"filter changed values": assert => {
},
}
}