提交 be70a397 编写于 作者: 1 100pah

test: migrate test lib myTransform to ts.

上级 1eaea81a
......@@ -138,6 +138,12 @@ async function run() {
];
await build(cfgs, opt.min, opt.sourcemap);
}
else if (opt.type === 'myTransform') {
const cfgs = [
config.createMyTransform()
];
await build(cfgs, opt.min, opt.sourcemap);
}
else {
const cfg = config.createECharts(opt);
await build([cfg], opt.min, opt.sourcemap);
......
......@@ -212,3 +212,27 @@ exports.createDataTool = function () {
}
};
};
exports.createMyTransform = function () {
let input = nodePath.resolve(ecDir, `test/lib/myTransform/src/index.ts`);
return {
plugins: preparePlugins({
clean: true
}, {
include: [
nodePath.resolve(ecDir, 'test/lib/myTransform/src/**/*.ts')
]
}),
input: input,
output: {
name: 'myTransform',
format: 'umd',
sourcemap: true,
file: nodePath.resolve(ecDir, `test/lib/myTransform/dist/myTransform.js`)
},
watch: {
include: [nodePath.resolve(ecDir, 'test/lib/myTransform/src/**')]
}
};
};
......@@ -42,7 +42,7 @@ function release() {
}
}
const argsList = ['', 'simple', 'common', 'extension'].map((type) => {
const argsList = ['', 'simple', 'common', 'extension', 'myTransform'].map((type) => {
return [
'--type',
type,
......
......@@ -83,7 +83,7 @@ export interface ExternalDataTransformResultItem {
*/
dimensions?: DimensionDefinitionLoose[];
}
interface ExternalDimensionDefinition extends Partial<DimensionDefinition> {
export interface ExternalDimensionDefinition extends Partial<DimensionDefinition> {
// Mandatory
index: DimensionIndex;
}
......
......@@ -27,7 +27,6 @@ under the License.
<script src='lib/jquery.min.js'></script>
<script src="../dist/echarts.js"></script>
<script src="lib/testHelper.js"></script>
<script src="lib/myTransform/aggregate.js"></script>
<script src="lib/transitionPlayer.js"></script>
<link rel="stylesheet" href="lib/reset.css" />
</head>
......@@ -76,9 +75,9 @@ under the License.
<script>
require(['echarts', 'ecStat'], function (echarts, ecStat) {
require(['echarts', 'ecStat', 'myTransform'], function (echarts, ecStat, myTransform) {
echarts.registerTransform(window.myTransform.aggregate);
echarts.registerTransform(myTransform.aggregate);
echarts.registerTransform(ecStat.transform.clustering);
......
......@@ -27,8 +27,6 @@ under the License.
<script src='lib/jquery.min.js'></script>
<script src="../dist/echarts.js"></script>
<script src="lib/testHelper.js"></script>
<script src="lib/myTransform/aggregate.js"></script>
<script src="lib/myTransform/id.js"></script>
<script src="lib/transitionPlayer.js"></script>
<link rel="stylesheet" href="lib/reset.css" />
</head>
......@@ -42,11 +40,11 @@ under the License.
<script>
require(['echarts', 'ecStat'], function (echarts, ecStat) {
require(['echarts', 'ecStat', 'myTransform'], function (echarts, ecStat, myTransform) {
$.get('data/life-expectancy-table.json', function (rawData) {
echarts.registerTransform(window.myTransform.aggregate);
echarts.registerTransform(window.myTransform.id);
echarts.registerTransform(myTransform.aggregate);
echarts.registerTransform(myTransform.id);
const COLORS = [
'#37A2DA', '#e06343', '#37a354', '#b55dba', '#b5bd48', '#8378EA', '#96BFFF'
......
......@@ -64,6 +64,7 @@
'echarts': ecDistPath,
'zrender': 'node_modules/zrender/dist/zrender',
'ecStat': 'test/lib/ecStat.min',
'myTransform': 'test/lib/myTransform/dist/myTransform',
// 'ecStat': 'http://localhost:8001/echarts/echarts-stat/dist/ecStat',
'geoJson': '../geoData/geoJson',
'theme': 'theme',
......
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
(function (exports) {
/**
* @usage
*
* ```js
* dataset: [{
* source: [
* ['aa', 'bb', 'cc', 'tag'],
* [12, 0.33, 5200, 'AA'],
* [21, 0.65, 7100, 'AA'],
* [51, 0.15, 1100, 'BB'],
* [71, 0.75, 9100, 'BB'],
* ...
* ]
* }, {
* transform: {
* type: 'my:aggregate',
* config: {
* resultDimensions: [
* // by default, use the same name with `from`.
* { from: 'aa', method: 'sum' },
* { from: 'bb', method: 'count' },
* { from: 'cc' }, // method by default: use the first value.
* { from: 'tag' }
* ],
* groupBy: 'tag'
* }
* }
* // Then the result data will be:
* // [
* // ['aa', 'bb', 'cc', 'tag'],
* // [12, 0.33, 5200, 'AA'],
* // [21, 0.65, 8100, 'BB'],
* // ...
* // ]
* }]
* ```
*/
var transform = {
type: 'myTransform:aggregate',
/**
* @param params
* @param params.config.resultDimensions Mandatory.
* {
* // Optional. The name of the result dimensions.
* // If not provided, inherit the name from `from`.
* name: DimensionName;
* // Mandatory. `from` is used to reference dimension from `source`.
* from: DimensionIndex | DimensionName;
* // Optional. Aggregate method. Currently only these method supported.
* // If not provided, use `'first'`.
* method: 'sum' | 'count' | 'first';
* }[]
* @param params.config.groupBy DimensionIndex | DimensionName Optional.
*/
transform: function (params) {
var upstream = params.upstream;
var config = params.config;
var resultDimensionsConfig = config.resultDimensions;
var resultDimInfoList = [];
var resultDimensions = [];
for (var i = 0; i < resultDimensionsConfig.length; i++) {
var resultDimInfoConfig = resultDimensionsConfig[i];
var resultDimInfo = upstream.getDimensionInfo(resultDimInfoConfig.from);
assert(resultDimInfo, 'Can not find dimension by `from`: ' + resultDimInfoConfig.from);
resultDimInfo.method = resultDimInfoConfig.method;
resultDimInfoList.push(resultDimInfo);
if (resultDimInfoConfig.name != null) {
resultDimInfo.name = resultDimInfoConfig.name;
}
resultDimensions.push(resultDimInfo.name);
}
var resultData = [];
var groupBy = config.groupBy;
var groupByDimInfo;
if (groupBy != null) {
var groupMap = {};
groupByDimInfo = upstream.getDimensionInfo(groupBy);
assert(groupByDimInfo, 'Can not find dimension by `groupBy`: ' + groupBy);
for (var dataIndex = 0, len = upstream.count(); dataIndex < len; dataIndex++) {
var groupByVal = upstream.retrieveValue(dataIndex, groupByDimInfo.index);
if (!groupMap.hasOwnProperty(groupByVal)) {
var newLine = createLine(upstream, dataIndex, resultDimInfoList, groupByDimInfo, groupByVal);
resultData.push(newLine);
groupMap[groupByVal] = newLine;
}
else {
var targetLine = groupMap[groupByVal];
aggregateLine(upstream, dataIndex, targetLine, resultDimInfoList, groupByDimInfo);
}
}
}
else {
var targetLine = createLine(upstream, 0, resultDimInfoList);
resultData.push(targetLine);
for (var dataIndex = 0, len = upstream.count(); dataIndex < len; dataIndex++) {
aggregateLine(upstream, dataIndex, targetLine, resultDimInfoList);
}
}
return {
dimensions: resultDimensions,
data: resultData
};
}
};
function createLine(upstream, dataIndex, resultDimInfoList, groupByDimInfo, groupByVal) {
var newLine = [];
for (var j = 0; j < resultDimInfoList.length; j++) {
var resultDimInfo = resultDimInfoList[j];
var method = resultDimInfo.method;
newLine[j] = (groupByDimInfo && resultDimInfo.index === groupByDimInfo.index)
? groupByVal
: (method === 'sum' || method === 'count')
? 0
// By default, method: 'first'
: upstream.retrieveValue(dataIndex, resultDimInfo.index);
}
return newLine;
}
function aggregateLine(upstream, dataIndex, targetLine, resultDimInfoList, groupByDimInfo) {
for (var j = 0; j < resultDimInfoList.length; j++) {
var resultDimInfo = resultDimInfoList[j];
var method = resultDimInfo.method;
if (!groupByDimInfo || resultDimInfo.index !== groupByDimInfo.index) {
if (method === 'sum') {
targetLine[j] += upstream.retrieveValue(dataIndex, resultDimInfo.index);
}
else if (method === 'count') {
targetLine[j] += 1;
}
}
}
}
function assert(cond, msg) {
if (!cond) {
throw new Error(msg || 'transition player error');
}
}
var myTransform = exports.myTransform = exports.myTransform || {};
myTransform.aggregate = transform;
})(this);
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = global || self, factory(global.myTransform = {}));
})(this, function (exports) {
'use strict';
var transform = {
type: 'myTransform:id',
transform: function (params) {
var upstream = params.upstream;
var config = params.config;
var dimensionIndex = config.dimensionIndex;
var dimensionName = config.dimensionName;
var dimsDef = upstream.cloneAllDimensionInfo();
dimsDef[dimensionIndex] = dimensionName;
var data = upstream.cloneRawData();
for (var i = 0, len = data.length; i < len; i++) {
var line = data[i];
line[dimensionIndex] = i;
}
return {
dimensions: dimsDef,
data: data
};
}
};
var arrayProto = Array.prototype;
var nativeSlice = arrayProto.slice;
var ctorFunction = function () {}.constructor;
var protoFunction = ctorFunction ? ctorFunction.prototype : null;
function bindPolyfill(func, context) {
var args = [];
for (var _i = 2; _i < arguments.length; _i++) {
args[_i - 2] = arguments[_i];
}
return function () {
return func.apply(context, args.concat(nativeSlice.call(arguments)));
};
}
var bind = protoFunction && isFunction(protoFunction.bind) ? protoFunction.call.bind(protoFunction.bind) : bindPolyfill;
function isFunction(value) {
return typeof value === 'function';
}
function assert(condition, message) {
if (!condition) {
throw new Error(message);
}
}
function hasOwn(own, prop) {
return own.hasOwnProperty(prop);
}
var METHOD_INTERNAL = {
'SUM': true,
'COUNT': true,
'FIRST': true,
'AVERAGE': true,
'Q1': true,
'Q2': true,
'Q3': true,
'MIN': true,
'MAX': true
};
var METHOD_ALIAS = {
MEDIAN: 'Q2'
};
var transform$1 = {
type: 'myTransform:aggregate',
transform: function (params) {
var upstream = params.upstream;
var config = params.config;
var dimWrap = prepareDimensions(config, upstream);
var resultDimInfoList = dimWrap.resultDimInfoList;
var resultDimensions = dimWrap.resultDimensions;
var groupByDimInfo = prepareGroupByDimInfo(config, upstream);
var finalResult = travel(groupByDimInfo, upstream, resultDimInfoList, createResultLine, aggregateResultLine);
return {
dimensions: resultDimensions,
data: finalResult.outList
};
}
};
function prepareDimensions(config, upstream) {
var resultDimensionsConfig = config.resultDimensions;
var resultDimInfoList = [];
var resultDimensions = [];
for (var i = 0; i < resultDimensionsConfig.length; i++) {
var resultDimInfoConfig = resultDimensionsConfig[i];
var resultDimInfo = upstream.getDimensionInfo(resultDimInfoConfig.from);
assert(resultDimInfo, 'Can not find dimension by `from`: ' + resultDimInfoConfig.from);
resultDimInfo.method = normalizeMethod(resultDimInfoConfig.method);
assert(resultDimInfo.method, 'method is required');
resultDimInfoList.push(resultDimInfo);
if (resultDimInfoConfig.name != null) {
resultDimInfo.name = resultDimInfoConfig.name;
}
resultDimensions.push(resultDimInfo.name);
}
return {
resultDimensions: resultDimensions,
resultDimInfoList: resultDimInfoList
};
}
function prepareGroupByDimInfo(config, upstream) {
var groupByConfig = config.groupBy;
var groupByDimInfo;
if (groupByConfig != null) {
groupByDimInfo = upstream.getDimensionInfo(groupByConfig);
assert(groupByDimInfo, 'Can not find dimension by `groupBy`: ' + groupByConfig);
}
return groupByDimInfo;
}
function travel(groupByDimInfo, upstream, resultDimInfoList, doCreate, doAggregate) {
var outList = [];
var groupMap;
if (groupByDimInfo) {
groupMap = {};
for (var dataIndex = 0, len = upstream.count(); dataIndex < len; dataIndex++) {
var groupByVal = upstream.retrieveValue(dataIndex, groupByDimInfo.index);
if (groupByVal == null) {
continue;
}
var groupByValStr = groupByVal + '';
if (!hasOwn(groupMap, groupByValStr)) {
var newLine = doCreate(upstream, dataIndex, resultDimInfoList, groupByDimInfo, groupByVal);
outList.push(newLine);
groupMap[groupByValStr] = newLine;
} else {
var targetLine = groupMap[groupByValStr];
doAggregate(upstream, dataIndex, targetLine, resultDimInfoList, groupByDimInfo);
}
}
} else {
var targetLine = doCreate(upstream, 0, resultDimInfoList);
outList.push(targetLine);
for (var dataIndex = 0, len = upstream.count(); dataIndex < len; dataIndex++) {
doAggregate(upstream, dataIndex, targetLine, resultDimInfoList);
}
}
return {
groupMap: groupMap,
outList: outList
};
}
function normalizeMethod(method) {
if (method == null) {
return 'FIRST';
}
var methodInternal = method.toUpperCase();
methodInternal = hasOwn(METHOD_ALIAS, methodInternal) ? METHOD_ALIAS[methodInternal] : methodInternal;
assert(hasOwn(METHOD_INTERNAL, methodInternal), "Illegal method " + method + ".");
return methodInternal;
}
var createResultLine = function (upstream, dataIndex, resultDimInfoList, groupByDimInfo, groupByVal) {
var newLine = [];
for (var j = 0; j < resultDimInfoList.length; j++) {
var resultDimInfo = resultDimInfoList[j];
var method = resultDimInfo.method;
newLine[j] = groupByDimInfo && resultDimInfo.index === groupByDimInfo.index ? groupByVal : method === 'SUM' || method === 'COUNT' ? 0 : upstream.retrieveValue(dataIndex, resultDimInfo.index);
}
return newLine;
};
var aggregateResultLine = function (upstream, dataIndex, targetLine, resultDimInfoList, groupByDimInfo) {
for (var j = 0; j < resultDimInfoList.length; j++) {
var resultDimInfo = resultDimInfoList[j];
var method = resultDimInfo.method;
if (groupByDimInfo && resultDimInfo.index === groupByDimInfo.index) {
continue;
}
if (method === 'SUM') {
targetLine[j] += upstream.retrieveValue(dataIndex, resultDimInfo.index);
} else if (method === 'COUNT') {
targetLine[j] += 1;
} else if (method === 'AVERAGE') {
targetLine[j] += upstream.retrieveValue(dataIndex, resultDimInfo.index) / 1;
}
}
};
exports.aggregate = transform$1;
exports.id = transform;
Object.defineProperty(exports, '__esModule', {
value: true
});
});
\ No newline at end of file
{"version":3,"file":"myTransform.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
\ No newline at end of file
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
(function (exports) {
/**
* @usage
*
* ```js
* dataset: [{
* source: [
* ['aa', 'bb', 'cc', 'tag'],
* [12, 0.33, 5200, 'AA'],
* [21, 0.65, 8100, 'AA'],
* ...
* ]
* }, {
* transform: {
* type: 'my:id',
* config: {
* dimensionIndex: 4,
* dimensionName: 'ID'
* }
* }
* // Then the result data will be:
* // [
* // ['aa', 'bb', 'cc', 'tag', 'ID'],
* // [12, 0.33, 5200, 'AA', 0],
* // [21, 0.65, 8100, 'BB', 1],
* // ...
* // ]
* }]
* ```
*/
var transform = {
type: 'myTransform:id',
/**
* @param params.config.dimensionIndex DimensionIndex
* Mandatory. Specify where to put the new id dimension.
* @param params.config.dimensionName DimensionName
* Optional. If not provided, left the dimension name not defined.
*/
transform: function (params) {
var upstream = params.upstream;
var config = params.config;
var dimensionIndex = config.dimensionIndex;
var dimensionName = config.dimensionName;
var dimsDef = upstream.cloneAllDimensionInfo();
dimsDef[dimensionIndex] = dimensionName;
var data = upstream.cloneRawData();
for (var i = 0, len = data.length; i < len; i++) {
var line = data[i];
line[dimensionIndex] = i;
}
return {
dimensions: dimsDef,
data: upstream.data
};
}
};
var myTransform = exports.myTransform = exports.myTransform || {};
myTransform.id = transform;
})(this);
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
# Note:
# If eslint does not work in VSCode, please check:
# (1) Whether "@typescript-eslint/eslint-plugin" and "@typescript-eslint/parser"
# are npm installed locally. Should better in the same version.
# (2) Whether "VSCode ESlint extension" is installed.
# (3) If the project folder is not the root folder of your working space, please
# config the "VSCode ESlint extension" in "settings":
# ```json
# "eslint.workingDirectories": [{"mode": "auto"}]
# ```
# Note that it should be "workingDirectories" rather than "WorkingDirectories".
parser: "@typescript-eslint/parser"
parserOptions:
ecmaVersion: 6
sourceType: module
ecmaFeatures:
modules: true
project: "tsconfig.json"
plugins: ["@typescript-eslint"]
env:
browser: true
node: true
es6: false
globals:
jQuery: false
Promise: false
__DEV__: false
extends: '../../../../.eslintrc-common.yaml'
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import {
DataTransformOption, ExternalDataTransform, ExternalSource, ExternalDimensionDefinition
} from '../../../../src/data/helper/transform';
import {
DimensionName, DimensionLoose, DimensionDefinitionLoose, OptionDataValue
} from '../../../../src/util/types';
import { hasOwn, assert } from 'zrender/src/core/util';
/**
* @usage
*
* ```js
* dataset: [{
* source: [
* ['aa', 'bb', 'cc', 'tag'],
* [12, 0.33, 5200, 'AA'],
* [21, 0.65, 7100, 'AA'],
* [51, 0.15, 1100, 'BB'],
* [71, 0.75, 9100, 'BB'],
* ...
* ]
* }, {
* transform: {
* type: 'my:aggregate',
* config: {
* resultDimensions: [
* // by default, use the same name with `from`.
* { from: 'aa', method: 'sum' },
* { from: 'bb', method: 'count' },
* { from: 'cc' }, // method by default: use the first value.
* { from: 'dd', method: 'Q1', boundIQR: 1 },
* { from: 'tag' }
* ],
* groupBy: 'tag'
* }
* }
* // Then the result data will be:
* // [
* // ['aa', 'bb', 'cc', 'tag'],
* // [12, 0.33, 5200, 'AA'],
* // [21, 0.65, 8100, 'BB'],
* // ...
* // ]
* }]
* ```
*
* Current supported methods (case insensitive):
* 'sum'
* 'count'
* 'average'
* 'Q1'
* 'Q3'
* 'Q2' or 'median'
* 'min'
* 'max'
*
* Current supported other arguments:
* boundIQR:
* Data less than min bound is outlier.
* default 1.5, means Q1 - 1.5 * (Q3 - Q1).
* If 'none'/0 passed, min bound will not be used.
*
*/
export interface AggregateTransformOption extends DataTransformOption {
type: 'myTransform:aggregate';
config: {
// Mandatory
resultDimensions: {
// Optional. The name of the result dimensions.
// If not provided, inherit the name from `from`.
name: DimensionName;
// Mandatory. `from` is used to reference dimension from `source`.
from: DimensionLoose;
// Optional. Aggregate method. Currently only these method supported.
// If not provided, use `'first'`.
method: AggregateMethodLoose;
}[];
// Optional
groupBy: DimensionLoose;
};
}
const METHOD_INTERNAL = {
'SUM': true,
'COUNT': true,
'FIRST': true,
'AVERAGE': true,
'Q1': true,
'Q2': true,
'Q3': true,
'MIN': true,
'MAX': true
} as const;
const METHOD_NEEDS_COLLECT = {
AVERAGE: true,
Q1: true,
Q2: true,
Q3: true
} as const;
const METHOD_ALIAS = {
MEDIAN: 'Q2'
} as const;
type AggregateMethodLoose =
AggregateMethodInternal
| 'sum' | 'count' | 'first' | 'average' | 'Q1' | 'Q2' | 'Q3' | 'median' | 'min' | 'max';
type AggregateMethodInternal = keyof typeof METHOD_INTERNAL;
interface ResultDimInfoInternal extends ExternalDimensionDefinition {
method: AggregateMethodInternal;
}
type CreateInTravel = (
upstream: ExternalSource,
dataIndex: number,
resultDimInfoList: ResultDimInfoInternal[],
groupByDimInfo?: ExternalDimensionDefinition,
groupByVal?: OptionDataValue
) => void;
type AggregateInTravel = (
upstream: ExternalSource,
dataIndex: number,
targetLine: unknown,
resultDimInfoList: ResultDimInfoInternal[],
groupByDimInfo?: ExternalDimensionDefinition
) => void;
export const transform: ExternalDataTransform<AggregateTransformOption> = {
type: 'myTransform:aggregate',
transform: function (params) {
const upstream = params.upstream;
const config = params.config;
const dimWrap = prepareDimensions(config, upstream);
const resultDimInfoList = dimWrap.resultDimInfoList;
const resultDimensions = dimWrap.resultDimensions;
const groupByDimInfo = prepareGroupByDimInfo(config, upstream);
// Collect
// const collectResult;
// const dimInfoListForCollect = makeDimInfoListForCollect(resultDimInfoList);
// if (dimInfoListForCollect.length) {
// collectResult = travel(groupByDimInfo, upstream, resultDimInfoList, doCreate, doAggregate);
// }
// Calculate
const finalResult = travel(
groupByDimInfo, upstream, resultDimInfoList, createResultLine, aggregateResultLine
);
return {
dimensions: resultDimensions,
data: finalResult.outList
};
}
};
function prepareDimensions(
config: AggregateTransformOption['config'],
upstream: ExternalSource
): {
resultDimInfoList: ResultDimInfoInternal[];
resultDimensions: DimensionDefinitionLoose[];
} {
const resultDimensionsConfig = config.resultDimensions;
const resultDimInfoList: ResultDimInfoInternal[] = [];
const resultDimensions: DimensionDefinitionLoose[] = [];
for (let i = 0; i < resultDimensionsConfig.length; i++) {
const resultDimInfoConfig = resultDimensionsConfig[i];
const resultDimInfo = upstream.getDimensionInfo(resultDimInfoConfig.from) as ResultDimInfoInternal;
assert(resultDimInfo, 'Can not find dimension by `from`: ' + resultDimInfoConfig.from);
resultDimInfo.method = normalizeMethod(resultDimInfoConfig.method);
assert(resultDimInfo.method, 'method is required');
resultDimInfoList.push(resultDimInfo);
if (resultDimInfoConfig.name != null) {
resultDimInfo.name = resultDimInfoConfig.name;
}
resultDimensions.push(resultDimInfo.name);
}
return { resultDimensions, resultDimInfoList };
}
function prepareGroupByDimInfo(
config: AggregateTransformOption['config'],
upstream: ExternalSource
): ExternalDimensionDefinition {
const groupByConfig = config.groupBy;
let groupByDimInfo;
if (groupByConfig != null) {
groupByDimInfo = upstream.getDimensionInfo(groupByConfig);
assert(groupByDimInfo, 'Can not find dimension by `groupBy`: ' + groupByConfig);
}
return groupByDimInfo;
}
function travel(
groupByDimInfo: ExternalDimensionDefinition,
upstream: ExternalSource,
resultDimInfoList: ResultDimInfoInternal[],
doCreate: CreateInTravel,
doAggregate: AggregateInTravel
): {
groupMap: { [groupVal in string]: unknown };
outList: unknown[];
} {
const outList: unknown[] = [];
let groupMap: { [groupVal in string]: unknown };
if (groupByDimInfo) {
groupMap = {};
for (let dataIndex = 0, len = upstream.count(); dataIndex < len; dataIndex++) {
const groupByVal = upstream.retrieveValue(dataIndex, groupByDimInfo.index);
// PENDING: when value is null/undefined
if (groupByVal == null) {
continue;
}
const groupByValStr = groupByVal + '';
if (!hasOwn(groupMap, groupByValStr)) {
const newLine = doCreate(upstream, dataIndex, resultDimInfoList, groupByDimInfo, groupByVal);
outList.push(newLine);
groupMap[groupByValStr] = newLine;
}
else {
const targetLine = groupMap[groupByValStr];
doAggregate(upstream, dataIndex, targetLine, resultDimInfoList, groupByDimInfo);
}
}
}
else {
const targetLine = doCreate(upstream, 0, resultDimInfoList);
outList.push(targetLine);
for (let dataIndex = 0, len = upstream.count(); dataIndex < len; dataIndex++) {
doAggregate(upstream, dataIndex, targetLine, resultDimInfoList);
}
}
return {
groupMap: groupMap,
outList: outList
};
}
// function makeDimInfoListForCollect(resultDimInfoList) {
// const dimInfoListForCollect = [];
// for (const j = 0; j < resultDimInfoList.length; j++) {
// const resultDimInfo = resultDimInfoList[j];
// const method = resultDimInfo.method;
// if (hasOwn(METHOD_NEEDS_COLLECT, method)) {
// dimInfoListForCollect.push(resultDimInfo);
// }
// }
// return dimInfoListForCollect;
// }
function normalizeMethod(method: AggregateMethodLoose): AggregateMethodInternal {
if (method == null) {
return 'FIRST';
}
let methodInternal = method.toUpperCase() as AggregateMethodInternal;
methodInternal = hasOwn(METHOD_ALIAS, methodInternal)
? METHOD_ALIAS[methodInternal as keyof typeof METHOD_ALIAS]
: methodInternal;
assert(hasOwn(METHOD_INTERNAL, methodInternal), `Illegal method ${method}.`);
return methodInternal;
}
const createResultLine: CreateInTravel = (
upstream, dataIndex, resultDimInfoList, groupByDimInfo, groupByVal
) => {
const newLine = [];
for (let j = 0; j < resultDimInfoList.length; j++) {
const resultDimInfo = resultDimInfoList[j];
const method = resultDimInfo.method;
newLine[j] = (groupByDimInfo && resultDimInfo.index === groupByDimInfo.index)
? groupByVal
: (method === 'SUM' || method === 'COUNT')
? 0
// By default, method: 'first'
: upstream.retrieveValue(dataIndex, resultDimInfo.index);
}
return newLine;
};
const aggregateResultLine: AggregateInTravel = (
upstream, dataIndex, targetLine: number[], resultDimInfoList, groupByDimInfo
) => {
for (let j = 0; j < resultDimInfoList.length; j++) {
const resultDimInfo = resultDimInfoList[j];
const method = resultDimInfo.method;
if (groupByDimInfo && resultDimInfo.index === groupByDimInfo.index) {
continue;
}
if (method === 'SUM') {
// FIXME: handle other types
targetLine[j] += upstream.retrieveValue(dataIndex, resultDimInfo.index) as number;
}
else if (method === 'COUNT') {
targetLine[j] += 1;
}
else if (method === 'AVERAGE') {
// FIXME: handle other types
targetLine[j] += upstream.retrieveValue(dataIndex, resultDimInfo.index) as number / 1;
}
}
};
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { ExternalDataTransform, DataTransformOption } from '../../../../src/data/helper/transform';
import { DimensionIndex, DimensionName, DimensionDefinitionLoose, OptionSourceDataArrayRows } from '../../../../src/util/types';
/**
* @usage
*
* ```js
* dataset: [{
* source: [
* ['aa', 'bb', 'cc', 'tag'],
* [12, 0.33, 5200, 'AA'],
* [21, 0.65, 8100, 'AA'],
* ...
* ]
* }, {
* transform: {
* type: 'my:id',
* config: {
* dimensionIndex: 4,
* dimensionName: 'ID'
* }
* }
* // Then the result data will be:
* // [
* // ['aa', 'bb', 'cc', 'tag', 'ID'],
* // [12, 0.33, 5200, 'AA', 0],
* // [21, 0.65, 8100, 'BB', 1],
* // ...
* // ]
* }]
* ```
*/
export interface IdTransformOption extends DataTransformOption {
type: 'myTransform:id';
config: {
// Mandatory. Specify where to put the new id dimension.
dimensionIndex: DimensionIndex;
// Optional. If not provided, left the dimension name not defined.
dimensionName: DimensionName;
};
}
export const transform: ExternalDataTransform<IdTransformOption> = {
type: 'myTransform:id',
transform: function (params) {
const upstream = params.upstream;
const config = params.config;
const dimensionIndex = config.dimensionIndex;
const dimensionName = config.dimensionName;
const dimsDef = upstream.cloneAllDimensionInfo() as DimensionDefinitionLoose[];
dimsDef[dimensionIndex] = dimensionName;
const data = upstream.cloneRawData() as OptionSourceDataArrayRows;
// TODO: support objectRows
for (let i = 0, len = data.length; i < len; i++) {
const line = data[i];
line[dimensionIndex] = i;
}
return {
dimensions: dimsDef,
data: data
};
}
};
export { transform as id } from './id';
export { transform as aggregate } from './aggregate';
......@@ -22,6 +22,7 @@
},
"include": [
"src/**/*.ts",
"extension-src/**/*.ts"
"extension-src/**/*.ts",
"test/lib/myTransform/src/**/*.ts"
]
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册