Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Class for compare previous/next arrays of data, detect enter/update/exit info
- // Inspired by https://github.com/d3/d3-selection#selection_join
- export type KeyFunction<T> = (datum: T) => string | symbol;
- export type DiffFunction<T> = (datum1: T, datum2: T) => boolean;
- export type UpdateInfo<T> = { before: T, after: T };
- export class DataComparer<T> {
- private currentData: T[];
- private currentDataByKey: Map<string | symbol, T>;
- private keyFn: KeyFunction<T>;
- private diffFn: DiffFunction<T>;
- private enter: T[];
- private update: UpdateInfo<T>[];
- private exit: T[];
- constructor() {
- this.keyFn = datum => JSON.stringify(datum);
- this.diffFn = datum => Boolean(datum);
- this.currentData = [];
- this.currentDataByKey = this.getDataByKey(this.currentData);
- this.enter = [];
- this.update = [];
- this.exit = [];
- }
- // Overloads
- public key(): KeyFunction<T>;
- public key(keyFn: KeyFunction<T>): this;
- // @NOTE: Set key function (or get current keyFn)
- public key(keyFn?: KeyFunction<T>) {
- if (!keyFn) return this.keyFn;
- this.keyFn = keyFn;
- this.currentDataByKey = this.getDataByKey(this.currentData);
- return this;
- }
- // Overloads
- public diff(): DiffFunction<T>;
- public diff(diffFn: DiffFunction<T>): this;
- // @NOTE: Set diff function (how to compare two datums) (or get current diffFn)
- public diff(diffFn?: DiffFunction<T>) {
- if (!diffFn) return this.diffFn;
- this.diffFn = diffFn;
- return this;
- }
- // @NOTE: Update data and compare
- public data(data: T[]) {
- const dataByKey = this.getDataByKey(data);
- const { enter, update, exit } = this.compare(this.currentDataByKey, dataByKey);
- this.enter = enter;
- this.update = update;
- this.exit = exit;
- this.currentData = data;
- this.currentDataByKey = dataByKey;
- return this;
- }
- // @NOTE: Get comparison info (enter/update/exit)
- public join(
- enter?: (data: T[]) => any,
- update?: (data: UpdateInfo<T>[]) => any,
- exit?: (data: T[]) => any,
- ) {
- if (enter) enter(this.enter);
- if (update) update(this.update);
- if (exit) exit(this.exit);
- return this;
- }
- // @NOTE: Calculate enter/update/exit arrays by key and diff functions
- private compare(before: Map<string | symbol, T>, after: Map<string | symbol, T>) {
- const enter: T[] = [];
- const update: UpdateInfo<T>[] = [];
- const exit: T[] = [];
- for (const datum of after.values()) {
- const key = this.keyFn(datum);
- // @NOTE: Added datums
- if (!before.has(key)) {
- enter.push(datum);
- }
- }
- for (const datum of before.values()) {
- const key = this.keyFn(datum);
- // @NOTE: Deleted datums
- if (!after.has(key)) {
- exit.push(datum);
- }
- // @NOTE: Updated datums
- if (after.has(key) && before.has(key)) {
- const newDatum = after.get(key);
- if (this.diffFn(datum, newDatum!)) {
- update.push({
- before: datum,
- after: newDatum!,
- });
- }
- }
- }
- return {
- enter,
- update,
- exit,
- };
- }
- private getDataByKey(data: T[]) {
- return new Map(data.map(datum => [this.keyFn(datum), datum]));
- }
- }
- // =========================== TEST ===========================
- type CustomDatum = {
- id: string;
- value: number;
- }
- const arrayComparer = new DataComparer<CustomDatum>()
- .key(datum => datum.id)
- .diff((oldDatum, newDatum) => oldDatum.value !== newDatum.value)
- ;
- const dataBefore: CustomDatum[] = [
- { id: '1', value: 11 },
- { id: '2', value: 22 },
- { id: '3', value: 33 },
- { id: '4', value: 44 },
- ];
- arrayComparer
- .data(dataBefore)
- .join(
- enter => console.log('enter', enter),
- update => console.log('update', update),
- exit => console.log('exit', exit),
- )
- ;
- // ...
- const dataAfter: CustomDatum[] = [
- { id: '3', value: -33 },
- { id: '4', value: -44 },
- { id: '5', value: 55 },
- { id: '6', value: 66 },
- ];
- arrayComparer
- .data(dataAfter)
- .join(
- enter => console.log('enter', enter),
- update => console.log('update', update),
- exit => console.log('exit', exit),
- )
- ;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement