Adding Merging Data
This commit is contained in:
parent
63491a5bef
commit
aa569c5d6b
67
lib/helpers/mapMethods.spec.ts
Normal file
67
lib/helpers/mapMethods.spec.ts
Normal file
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* @author Martin Karkowski
|
||||
* @email m.karkowski@zema.de
|
||||
* @create date 2021-11-13 08:17:19
|
||||
* @modify date 2021-11-13 09:44:51
|
||||
* @desc [description]
|
||||
*/
|
||||
|
||||
import { assert } from "chai";
|
||||
import "chai/register-should";
|
||||
import { describe, it } from "mocha";
|
||||
import { extractUniqueValues } from "./mapMethods";
|
||||
|
||||
describe("mapMethods", function () {
|
||||
// Describe the required Test:
|
||||
|
||||
describe("extractUniqueValues", function () {
|
||||
it("simple-map", function () {
|
||||
const m = new Map<string, string>();
|
||||
m.set("a", "b");
|
||||
m.set("b", "b");
|
||||
|
||||
const result = extractUniqueValues(m);
|
||||
assert.isTrue(
|
||||
result.size === 1,
|
||||
"Elements have the same identity, but should be differend"
|
||||
);
|
||||
assert.isTrue([...result][0] === "b", "Element is element");
|
||||
});
|
||||
it("nested-map", function () {
|
||||
const m = new Map<string, { a: string }>();
|
||||
m.set("a", { a: "b" });
|
||||
m.set("b", { a: "b" });
|
||||
const result = extractUniqueValues(m, "a");
|
||||
|
||||
assert.isTrue(
|
||||
result.size === 1,
|
||||
"Elements have the same identity, but should be differend"
|
||||
);
|
||||
assert.isTrue([...result][0] === "b", "Element is element");
|
||||
});
|
||||
it("nested-array", function () {
|
||||
const m = new Map<string, { a: string[] }>();
|
||||
m.set("a", { a: ["b"] });
|
||||
m.set("b", { a: ["b"] });
|
||||
const result = extractUniqueValues(m, "a");
|
||||
|
||||
assert.isTrue(
|
||||
result.size === 1,
|
||||
"Elements have the same identity, but should be differend"
|
||||
);
|
||||
assert.isTrue([...result][0] === "b", "Element is element");
|
||||
});
|
||||
it("nested-array multiple elements", function () {
|
||||
const m = new Map<string, { a: string[] }>();
|
||||
m.set("a", { a: ["b"] });
|
||||
m.set("b", { a: ["c", "d"] });
|
||||
const result = extractUniqueValues(m, "a");
|
||||
|
||||
assert.isTrue(
|
||||
result.size === 3,
|
||||
"Elements have the same identity, but should be differend"
|
||||
);
|
||||
assert.deepEqual(["b", "c", "d"], [...result], "Items are missing");
|
||||
});
|
||||
});
|
||||
});
|
103
lib/helpers/mapMethods.ts
Normal file
103
lib/helpers/mapMethods.ts
Normal file
@ -0,0 +1,103 @@
|
||||
/**
|
||||
* @author Martin Karkowski
|
||||
* @email m.karkowski@zema.de
|
||||
* @create date 2021-11-23 11:21:57
|
||||
* @modify date 2021-11-23 11:21:57
|
||||
* @desc [description]
|
||||
*/
|
||||
|
||||
import { rgetattr } from "./objectMethods";
|
||||
|
||||
const __sentinal = {
|
||||
unique: "value",
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts the unique values of an map.
|
||||
*
|
||||
* @author M.Karkowski
|
||||
* @export
|
||||
* @template K
|
||||
* @template V
|
||||
* @param {Map<K, V>} map
|
||||
* @return {*} {Set<V>}
|
||||
*/
|
||||
export function extractUniqueValues<D>(map: Map<any, any>, path = ""): Set<D> {
|
||||
return new Set(extractValues(map, path));
|
||||
}
|
||||
|
||||
export function extractValues<D, K>(map: Map<K, any>, path = ""): Array<D> {
|
||||
const s = new Array<D>();
|
||||
|
||||
for (const v of map.values()) {
|
||||
if (path) {
|
||||
const data: D | typeof __sentinal = rgetattr(v, path, __sentinal);
|
||||
if (data !== __sentinal) {
|
||||
if (Array.isArray(data)) {
|
||||
data.map((item) => s.push(item));
|
||||
} else {
|
||||
s.push(data as D);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s.push(v as any as D);
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @author M.Karkowski
|
||||
* @export
|
||||
* @template D
|
||||
* @template K
|
||||
* @param {Map<K, any>} map
|
||||
* @param {string} [path=""]
|
||||
* @return {*} {Map<K, D>}
|
||||
*/
|
||||
export function transformValues<D, K = any>(
|
||||
map: Map<K, any>,
|
||||
path = ""
|
||||
): Map<K, D> {
|
||||
const m = new Map<K, D>();
|
||||
|
||||
for (const [k, v] of map.entries()) {
|
||||
if (path) {
|
||||
const data: D | typeof __sentinal = rgetattr(v, path, __sentinal);
|
||||
if (data !== __sentinal) {
|
||||
m.set(k, data as D);
|
||||
}
|
||||
} else {
|
||||
m.set(k, v as any as D);
|
||||
}
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverses the given map.
|
||||
*
|
||||
* @author M.Karkowski
|
||||
* @export
|
||||
* @template K
|
||||
* @template V
|
||||
* @param {Map<K, V>} map
|
||||
* @return {*} {Map<V, Set<K>>}
|
||||
*/
|
||||
export function reverse<K, V>(map: Map<K, V>): Map<V, Set<K>> {
|
||||
const m = new Map<V, Set<K>>();
|
||||
|
||||
for (const [k, v] of map.entries()) {
|
||||
if (!m.has(v)) {
|
||||
m.set(v, new Set());
|
||||
}
|
||||
|
||||
m.get(v).add(k);
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
86
lib/helpers/mergedData.spec.ts
Normal file
86
lib/helpers/mergedData.spec.ts
Normal file
@ -0,0 +1,86 @@
|
||||
/**
|
||||
* @author Martin Karkowski
|
||||
* @email m.karkowski@zema.de
|
||||
* @create date 2021-11-13 08:17:19
|
||||
* @modify date 2021-11-13 09:44:51
|
||||
* @desc [description]
|
||||
*/
|
||||
|
||||
import { assert } from "chai";
|
||||
import "chai/register-should";
|
||||
import { describe, it } from "mocha";
|
||||
import { extractUniqueValues } from "./mapMethods";
|
||||
import { MergeData } from "./mergedData";
|
||||
|
||||
describe("mergedData", function () {
|
||||
// Describe the required Test:
|
||||
it("data subscription", function (done) {
|
||||
const m = new Map<string, string>();
|
||||
const d = new MergeData(m, (m) => extractUniqueValues(m));
|
||||
m.set("a", "b");
|
||||
m.set("b", "b");
|
||||
|
||||
d.update();
|
||||
|
||||
d.data.subscribe((result) => {
|
||||
assert.isTrue(
|
||||
result.length === 1,
|
||||
"Elements have the same identity, but should be differend"
|
||||
);
|
||||
assert.isTrue([...result][0] === "b", "Element is element");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("data subscription. Update called twice", function (done) {
|
||||
const m = new Map<string, string>();
|
||||
const d = new MergeData(m, (m) => extractUniqueValues(m));
|
||||
m.set("a", "b");
|
||||
m.set("b", "b");
|
||||
d.update(m);
|
||||
d.data.subscribe((result) => {
|
||||
assert.isTrue(
|
||||
result.length === 1,
|
||||
"Elements have the same identity, but should be differend"
|
||||
);
|
||||
assert.isTrue([...result][0] === "b", "Element is element");
|
||||
done();
|
||||
});
|
||||
d.update(m);
|
||||
});
|
||||
|
||||
it("onchange subscription: added", function (done) {
|
||||
const m = new Map<string, string>();
|
||||
const d = new MergeData(m, (m) => extractUniqueValues(m));
|
||||
m.set("a", "b");
|
||||
m.set("b", "b");
|
||||
|
||||
d.onChange.subscribe((result) => {
|
||||
assert.isTrue(
|
||||
result.added.length === 1,
|
||||
"Elements have the same identity, but should be differend"
|
||||
);
|
||||
assert.isTrue([...result.added][0] === "b", "Element is element");
|
||||
done();
|
||||
});
|
||||
|
||||
d.update(m);
|
||||
});
|
||||
|
||||
it("onchange subscription: removed", function (done) {
|
||||
const m = new Map<string, string>();
|
||||
const d = new MergeData(m, (m) => extractUniqueValues(m));
|
||||
m.set("a", "b");
|
||||
m.set("b", "b");
|
||||
|
||||
d.onChange.subscribe((result) => {
|
||||
assert.isTrue(
|
||||
result.removed.length === 0,
|
||||
"Elements have the same identity, but should be differend"
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
d.update(m);
|
||||
});
|
||||
});
|
121
lib/helpers/mergedData.ts
Normal file
121
lib/helpers/mergedData.ts
Normal file
@ -0,0 +1,121 @@
|
||||
/**
|
||||
* @author Martin Karkowski
|
||||
* @email m.karkowski@zema.de
|
||||
* @create date 2021-11-23 11:04:49
|
||||
* @modify date 2021-11-23 11:04:49
|
||||
* @desc [description]
|
||||
*/
|
||||
|
||||
import { NopeEventEmitter } from "../eventEmitter/nopeEventEmitter";
|
||||
import { determineDifference } from "../helpers/setMethods";
|
||||
import { NopeObservable } from "../observables/nopeObservable";
|
||||
import { INopeEventEmitter, INopeObservable } from "../types/nope";
|
||||
import { IMergeData } from "../types/nope/nopeHelpers.interface";
|
||||
import { countElements } from "./arrayMethods";
|
||||
import {
|
||||
extractUniqueValues,
|
||||
extractValues,
|
||||
reverse,
|
||||
transformValues,
|
||||
} from "./mapMethods";
|
||||
|
||||
export class MergeData<T, D = any> implements IMergeData<T, D> {
|
||||
/**
|
||||
* Element which will trig implements IMergeDatager an event containing the changes
|
||||
*
|
||||
* @author M.Karkowski
|
||||
* @type {INopeEventEmitter<{
|
||||
* added: T[],
|
||||
* removed: T[]
|
||||
* }>}
|
||||
* @memberof MergeData
|
||||
*/
|
||||
readonly onChange: INopeEventEmitter<{
|
||||
added: T[];
|
||||
removed: T[];
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Contains the current data.
|
||||
*
|
||||
* @author M.Karkowski
|
||||
* @type {INopeObservable<T[]>}
|
||||
* @memberof MergeData
|
||||
*/
|
||||
readonly data: INopeObservable<T[]>;
|
||||
|
||||
constructor(
|
||||
public originalData: D,
|
||||
protected _extractData: (data: D) => Set<T>
|
||||
) {
|
||||
this.onChange = new NopeEventEmitter();
|
||||
this.data = new NopeObservable();
|
||||
this.data.setContent([]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the underlying data.
|
||||
*
|
||||
* @author M.Karkowski
|
||||
* @param {*} [data=this.originalData]
|
||||
* @memberof MergeData
|
||||
*/
|
||||
public update(data: D = null): void {
|
||||
if (data !== null) {
|
||||
this.originalData = data;
|
||||
}
|
||||
|
||||
const afterAdding = this._extractData(this.originalData);
|
||||
const diff = determineDifference(
|
||||
new Set(this.data.getContent()),
|
||||
afterAdding
|
||||
);
|
||||
|
||||
if (diff.removed.size > 0 || diff.added.size > 0) {
|
||||
// Update the currently used subscriptions
|
||||
this.data.setContent(Array.from(afterAdding));
|
||||
// Now emit, that there is a new subscription.
|
||||
this.onChange.emit({
|
||||
added: Array.from(diff.added),
|
||||
removed: Array.from(diff.removed),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class MapBasedMergeData<T, K, V>
|
||||
extends MergeData<T, Map<K, V>>
|
||||
implements IMergeData<T, Map<K, V>>
|
||||
{
|
||||
public amountOf: Map<T, number>;
|
||||
public simplified: Map<K, T>;
|
||||
public reverseSimplified: Map<T, Set<K>>;
|
||||
|
||||
constructor(originalData: Map<K, V>, protected _path = "") {
|
||||
super(originalData, (m) => {
|
||||
return extractUniqueValues(m, _path);
|
||||
});
|
||||
|
||||
this.amountOf = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the underlying data.
|
||||
*
|
||||
* @author M.Karkowski
|
||||
* @param {*} [data=this.originalData]
|
||||
* @memberof MergeData
|
||||
*/
|
||||
public update(data: Map<K, V> = null): void {
|
||||
if (data !== null) {
|
||||
this.originalData = data;
|
||||
}
|
||||
|
||||
// Now lets update the amount of the data:
|
||||
this.amountOf = countElements(extractValues(data, this._path));
|
||||
this.simplified = transformValues(this.originalData, this._path);
|
||||
this.reverseSimplified = reverse(this.simplified);
|
||||
|
||||
super.update(data);
|
||||
}
|
||||
}
|
25
lib/types/nope/nopeHelpers.interface.ts
Normal file
25
lib/types/nope/nopeHelpers.interface.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { INopeEventEmitter } from "./nopeEventEmitter.interface";
|
||||
import { INopeObservable } from "./nopeObservable.interface";
|
||||
|
||||
/**
|
||||
* @author Martin Karkowski
|
||||
* @email m.karkowski@zema.de
|
||||
* @create date 2021-11-23 12:31:01
|
||||
* @modify date 2021-11-23 12:31:01
|
||||
* @desc [description]
|
||||
*/
|
||||
export interface IMergeData<T = any, K = any> {
|
||||
onChange: INopeEventEmitter<{
|
||||
added: T[];
|
||||
removed: T[];
|
||||
}>;
|
||||
data: INopeObservable<T[]>;
|
||||
update(data?: K): void;
|
||||
}
|
||||
|
||||
export interface IMapBasedMergeData<T = any, K = any, V = any>
|
||||
extends IMergeData<T, Map<K, V>> {
|
||||
amountOf: Map<T, number>;
|
||||
simplified: Map<K, T>;
|
||||
reverseSimplified: Map<T, Set<K>>;
|
||||
}
|
Loading…
Reference in New Issue
Block a user