dasf-web
Version:
Web frontend components for the data analytics software framework (DASF)
209 lines (175 loc) • 7.74 kB
text/typescript
import HashMap from "../../util/HashMap";
import ArrayUtils from '../../util/ArrayUtils';
export default class SprDatabase {
private inputParametersMap: Map<string, string[]>;
private outputRangeMap: Map<string, number[]>;
public constructor(private dataMap: HashMap<object>, private inputParameters: string[], private outputParameters: string[],
uniqueInputParameterMap: Map<string, Set<string>>, private labelMap : Map<string, Map<string, number>>) {
this.inputParametersMap = this.setsToArrays(uniqueInputParameterMap);
this.outputRangeMap = this.determineOutputRanges(dataMap, outputParameters);
}
private setsToArrays(setMap: Map<string, Set<string>>): Map<string, string[]> {
let arrayMap: Map<string, string[]> = new Map();
for (let [key, set] of setMap) {
let valueArray: string[] = [...set];
arrayMap.set(key, valueArray);
}
return arrayMap;
}
private determineOutputRanges(dataMap: HashMap<object>, outputParameters: string[]): Map<string, number[]> {
let rangeMap: Map<string, number[]> = new Map();
// loop over all outputs and determine min/max ranges
for (let output of dataMap.values()) {
for (let outputParameter of outputParameters) {
const outputValue: number | string = output[outputParameter];
let value: number = NaN;
if(this.labelMap.has(outputParameter)) {
value = this.getLabelMapping(outputParameter, outputValue as string);
} else {
value = outputValue as number;
}
if (isNaN(value)) {
// ignore nan values
continue;
}
if (rangeMap.has(outputParameter)) {
// update range
let range: number[] = rangeMap.get(outputParameter);
range[0] = Math.min(range[0], value);
range[1] = Math.max(range[1], value);
} else {
// init range
rangeMap.set(outputParameter, [value, value]);
}
}
}
//check if every output parameter has a rangemap (in case of all NaN values add range [0,0])
outputParameters.forEach(element => {
if(!rangeMap.has(element)){
rangeMap.set(element, [0, 0]);
}
});
return rangeMap;
}
public getOutputValueRange(outputParameter: string): number[] {
return this.outputRangeMap.get(outputParameter);
}
/**
* Returns the list of available input parameters
*/
public getInputParameters(): string[] {
return ArrayUtils.clone(this.inputParameters);
}
public updateInputParameterValue(inputParameter: string, oldValue: string, newValue: string): void {
if (!oldValue) {
return;
}
let values: string[] = this.inputParametersMap.get(inputParameter);
if (values) {
var index = values.indexOf(oldValue);
if (~index) {
values[index] = newValue;
}
this.inputParametersMap.set(inputParameter, values);
}
// update datamap keys
for (let key of [...this.dataMap.keys()]) {
if (key[inputParameter] == oldValue) {
// update the key
let newKey = Object.assign({}, key);
newKey[inputParameter] = newValue;
this.dataMap.updateKey(key, newKey);
}
}
}
/**
* Returns the list of available output parameters
*/
public getOutputParameters(): string[] {
return ArrayUtils.clone(this.outputParameters);
}
public addNewOutputParameter(outputParameterName: string, getValue: (outputValues: object) => number): void {
for(let outputValue of this.dataMap.values()) {
let newValue = getValue(outputValue);
if(newValue == null){
return;
}
outputValue[outputParameterName] = newValue;
}
// add new output parameter to output parameter list
this.outputParameters.push(outputParameterName);
this.outputRangeMap = this.determineOutputRanges(this.dataMap, this.outputParameters);
}
/**
* Returns a list of unique values for the given parameter
*
* @param parameter - requested input parameter
*/
public getUniqueInputParameters(parameter: string): string[] {
return this.inputParametersMap.get(parameter);
}
/**
* Traverses the input-output relations in the order of the given parameters array, considering the fixedParameters mapping
* @param parameters - permutation of input parameters
* @param fixedParameters - mapping of fixed parameter values
* @param callbackfn - callback called for each input-output relation
*/
public foreach(parameters: string[], fixedParameters: Map<string, string>, callbackfn: (input: object, output: object) => void): void {
// create key instance and traverse the parameters
let key: object = {};
// this.fixedParams.clear;
if (fixedParameters && fixedParameters.size > 0) {
for (let [parameter, value] of fixedParameters.entries()) {
key[parameter] = value;
// this.fixedParams.set(parameter, value);
}
}
// ignore the fixed parameters if in parameters
let parameterList: string[] = [];
for (let p of parameters) {
if (!fixedParameters.has(p)) {
parameterList.push(p);
}
}
// TODO: ensure valid parameters list (must be a permutation of 'this.inputparameters')
this.traverseParameters(key, parameterList, callbackfn);
}
private traverseParameters(key: object, parameters: string[], callbackfn: (input: object, output: object) => void): void {
// remove next parameter from stack
let p = parameters[0];
// iterate over all unique values of this parameter
for (let parameterValue of this.inputParametersMap.get(p)) {
key[p] = parameterValue;
// did we reach the end of the parameter stack?
if (parameters.length == 1) {
// last parameter - trigger callback
let output = this.dataMap.get(key);
if (output) {
callbackfn(Object.assign({}, key), output)
} else {
// FIXME: here we could allow sparse data and simply skip non existing entries
throw new Error('invalid data mapping, no output for input found: ' + JSON.stringify(key));
}
} else {
// still parameters left on the stack - continue traversing
this.traverseParameters(key, parameters.slice(1), callbackfn);
}
}
}
/**
* Provides a label mapping with the following structure:
* ['outputKey' => ['label' => number]]
* @returns
*/
public getLabelMap(): Map<string, Map<string, number>> {
return this.labelMap;
}
public getLabelMapping(outputKey: string, outputValue: string): number {
if(this.labelMap && this.labelMap.has(outputKey) && this.labelMap.get(outputKey).has(outputValue)) {
return this.labelMap.get(outputKey).get(outputValue);
} else {
console.warn('unknown label mapping requested for ', [outputKey, outputValue]);
return Number.NaN;
}
}
}