nope-js-browser
Version:
NoPE Runtime for the Browser. For nodejs please use nope-js-node
265 lines (264 loc) • 8.71 kB
JavaScript
/**
* @author Martin Karkowski
* @email m.karkowski@zema.de
* @desc [description]
*/
import { convertData, deepEqual, rgetattr, rqueryAttr, SPLITCHAR, } from "./objectMethods";
import { getLeastCommonPathSegment } from "./path";
const __sentinal = {
unique: "value",
};
/**
* Extracts the unique values of an map.
*
* @author M.Karkowski
* @export
* @template D Return Type
* @template K The Key of the Map
* @template V The Value of the Map
* @param {Map<K, V>} map The Map
* @param {string} [path=""] The Path of the Data to extract.
* @param {string} [pathKey=null] The Path of the unique key. If set to `null` -> The Item is selected directly.
* @return {Set<D>}
*/
export function extractUniqueValues(map, path = "", pathKey = null) {
if (pathKey === null) {
pathKey = path;
}
if (path !== pathKey) {
// Get the Common segment of the item.
let commonSegment = getLeastCommonPathSegment([path, pathKey], {
considerSingleLevel: false,
considerMultiLevel: false,
});
if (commonSegment == false) {
commonSegment = "";
}
const commonSegmentLength = commonSegment.split(SPLITCHAR).length;
const _relPathContent = path
.split(SPLITCHAR)
.slice(commonSegmentLength)
.join(SPLITCHAR);
const _relPathKey = pathKey
.split(SPLITCHAR)
.slice(commonSegmentLength)
.join(SPLITCHAR);
// Now use that segment to extract the common data.
const items = extractValues(map, commonSegment);
const itemKeys = new Set();
const ret = [];
for (const item of items) {
const key = _relPathKey ? rgetattr(item, _relPathKey) : item;
const data = _relPathContent ? rgetattr(item, _relPathContent) : item;
if (!itemKeys.has(key)) {
itemKeys.add(key);
ret.push(data);
}
}
return new Set(ret);
}
return new Set(extractValues(map, path));
}
/**
* Helper to extract values of the map. Therefore the path must be provided.
* @param map
* @param path
* @returns
*/
export function extractValues(map, path = "") {
const s = new Array();
for (const v of map.values()) {
if (path) {
const data = rqueryAttr(v, path);
data.map((item) => {
return s.push(item.data);
});
}
else {
// Add the item.
s.push(v);
}
}
return s;
}
/**
* Transform the values.
*
*
* @author M.Karkowski
* @export
* @template ExtractedValue
* @template ExtractedKey
* @template OriginalKey
* @param {Map<OriginalKey, any>} map
* @param {string} [pathExtractedValue=""]
* @param {string} [pathExtractedKey=null] Additional Path of a Key.
* @return {*} {Map<ExtractedKey, ExtractedData>}
*/
export function tranformMap(map, pathExtractedValue, pathExtractedKey, equals = deepEqual) {
const keyMapping = new Map();
const reverseKeyMapping = new Map();
const conflicts = new Map();
const extractedMap = new Map();
const orgKeyToExtractedValue = new Map();
const amountOf = new Map();
const props = [];
let onlyValidProps = true;
if (typeof pathExtractedKey === "string") {
props.push({
key: "key",
query: pathExtractedKey,
});
onlyValidProps = onlyValidProps && pathExtractedKey.length > 0;
}
else {
onlyValidProps = false;
}
if (typeof pathExtractedValue === "string") {
props.push({
key: "value",
query: pathExtractedValue,
});
onlyValidProps = onlyValidProps && pathExtractedValue.length > 0;
}
else {
onlyValidProps = false;
}
// Iterate over the Entries of the Map.
// then we will extract the data stored in the Value.
for (const [k, v] of map.entries()) {
let extracted = [];
if (onlyValidProps) {
extracted = convertData(v, props);
}
else {
const data = {
key: null,
value: null,
};
// We migt adapt the key and the Value. Therefore we will use
// the next if statements
if (typeof pathExtractedKey === "string") {
if (pathExtractedKey.length > 0) {
data.key = rqueryAttr(v, pathExtractedKey).map((item) => item.data);
}
else {
data.key = [v];
}
}
else {
data.key = [k];
}
if (typeof pathExtractedValue === "string") {
if (pathExtractedValue.length > 0) {
data.value = rqueryAttr(v, pathExtractedValue).map((item) => item.data);
}
else {
data.value = [v];
}
}
else {
data.value = [v];
}
// For every key push the data.
for (const key of data.key) {
data.value.map((item) => extracted.push({
key: key,
value: item,
}));
}
}
// Store the Key.
keyMapping.set(k, new Set());
orgKeyToExtractedValue.set(k, new Set());
for (const item of extracted) {
if (extractedMap.has(item.key)) {
// If the extracted new key has already been defined,
// we have to determine whether the stored item matches
// the allready provided definition.
if (!equals(extractedMap.get(item.key), item.value)) {
// Conflict detected
if (!conflicts.has(item.key)) {
conflicts.set(item.key, new Set());
}
// Store the conflict.
conflicts.get(item.key).add(item.value);
conflicts.get(item.key).add(extractedMap.get(item.key));
}
else {
// Store the determined amount.
amountOf.set(item.key, (amountOf.get(item.key) || 0) + 1);
}
}
else {
// Store the item.
extractedMap.set(item.key, item.value);
// Store the determined amount.
amountOf.set(item.key, (amountOf.get(item.key) || 0) + 1);
}
// If the reverse haven't been set ==> create it.
if (!reverseKeyMapping.has(item.key)) {
reverseKeyMapping.set(item.key, new Set());
}
// Store the mapping of new-key --> org-key.
reverseKeyMapping.get(item.key).add(k);
// Store the mapping of org-key --> new-key.
keyMapping.get(k).add(item.key);
orgKeyToExtractedValue.get(k).add(item.value);
}
}
return {
extractedMap,
keyMapping,
conflicts,
keyMappingReverse: reverseKeyMapping,
orgKeyToExtractedValue,
amountOf,
};
}
/**
* Reverses the given map.
*
* If the path is provided, the Data is extracted based on the given path.
* If the `pathKey`, a different Key is used.
*
* @author M.Karkowski
* @export
* @template K
* @template V
* @param {Map<any,any>} map
* @param {string} [path=""]
* @param {string} [pathKey=null]
* @return {*} {Map<V, Set<K>>}
*/
export function reverse(map, path = "", pathKey = null) {
const m = new Map();
if (pathKey === null) {
pathKey = path;
}
for (const [k, v] of map.entries()) {
let keyToUse = k;
if (pathKey) {
keyToUse = rgetattr(v, pathKey, __sentinal);
}
let valueToUse = v;
if (path) {
valueToUse = rgetattr(v, path, __sentinal);
}
if (Array.isArray(valueToUse)) {
for (const _v of valueToUse) {
if (!m.has(_v)) {
m.set(_v, new Set());
}
m.get(_v).add(keyToUse);
}
}
else {
if (!m.has(valueToUse)) {
m.set(valueToUse, new Set());
}
m.get(valueToUse).add(keyToUse);
}
}
return m;
}