/*
|
* 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, ExternalDataTransformResultItem
|
} from '../../data/helper/transform';
|
import {
|
DimensionLoose, DimensionIndex, OptionDataValue, SOURCE_FORMAT_ARRAY_ROWS, SOURCE_FORMAT_OBJECT_ROWS
|
} from '../../util/types';
|
import { makePrintable, throwError } from '../../util/log';
|
import { each } from 'zrender/src/core/util';
|
import { normalizeToArray } from '../../util/model';
|
import {
|
RawValueParserType, getRawValueParser, SortOrderComparator
|
} from '../../data/helper/dataValueHelper';
|
|
/**
|
* @usage
|
*
|
* ```js
|
* transform: {
|
* type: 'sort',
|
* config: { dimension: 'score', order: 'asc' }
|
* }
|
* transform: {
|
* type: 'sort',
|
* config: [
|
* { dimension: 1, order: 'asc' },
|
* { dimension: 'age', order: 'desc' }
|
* ]
|
* }
|
* ```
|
*/
|
|
export interface SortTransformOption extends DataTransformOption {
|
type: 'sort';
|
config: OrderExpression | OrderExpression[];
|
}
|
|
// PENDING: whether support { dimension: 'score', order: 'asc' } ?
|
type OrderExpression = {
|
dimension: DimensionLoose;
|
order: 'asc' | 'desc';
|
parser?: RawValueParserType;
|
// The meansing of "incomparable": see [SORT_COMPARISON_RULE]
|
// in `data/helper/dataValueHelper.ts`
|
incomparable?: 'min' | 'max';
|
};
|
|
|
let sampleLog = '';
|
if (__DEV__) {
|
sampleLog = [
|
'Valid config is like:',
|
'{ dimension: "age", order: "asc" }',
|
'or [{ dimension: "age", order: "asc"], { dimension: "date", order: "desc" }]'
|
].join(' ');
|
}
|
|
|
export const sortTransform: ExternalDataTransform<SortTransformOption> = {
|
|
type: 'echarts:sort',
|
|
transform: function (params) {
|
const upstream = params.upstream;
|
const config = params.config;
|
let errMsg = '';
|
|
// Normalize
|
// const orderExprList: OrderExpression[] = isArray(config[0])
|
// ? config as OrderExpression[]
|
// : [config as OrderExpression];
|
const orderExprList: OrderExpression[] = normalizeToArray(config);
|
|
if (!orderExprList.length) {
|
if (__DEV__) {
|
errMsg = 'Empty `config` in sort transform.';
|
}
|
throwError(errMsg);
|
}
|
|
const orderDefList: {
|
dimIdx: DimensionIndex;
|
parser: ReturnType<typeof getRawValueParser>;
|
comparator: SortOrderComparator
|
}[] = [];
|
each(orderExprList, function (orderExpr) {
|
const dimLoose = orderExpr.dimension;
|
const order = orderExpr.order;
|
const parserName = orderExpr.parser;
|
const incomparable = orderExpr.incomparable;
|
|
if (dimLoose == null) {
|
if (__DEV__) {
|
errMsg = 'Sort transform config must has "dimension" specified.' + sampleLog;
|
}
|
throwError(errMsg);
|
}
|
|
if (order !== 'asc' && order !== 'desc') {
|
if (__DEV__) {
|
errMsg = 'Sort transform config must has "order" specified.' + sampleLog;
|
}
|
throwError(errMsg);
|
}
|
|
if (incomparable && (incomparable !== 'min' && incomparable !== 'max')) {
|
let errMsg = '';
|
if (__DEV__) {
|
errMsg = 'incomparable must be "min" or "max" rather than "' + incomparable + '".';
|
}
|
throwError(errMsg);
|
}
|
if (order !== 'asc' && order !== 'desc') {
|
let errMsg = '';
|
if (__DEV__) {
|
errMsg = 'order must be "asc" or "desc" rather than "' + order + '".';
|
}
|
throwError(errMsg);
|
}
|
|
const dimInfo = upstream.getDimensionInfo(dimLoose);
|
if (!dimInfo) {
|
if (__DEV__) {
|
errMsg = makePrintable(
|
'Can not find dimension info via: ' + dimLoose + '.\n',
|
'Existing dimensions: ', upstream.cloneAllDimensionInfo(), '.\n',
|
'Illegal config:', orderExpr, '.\n'
|
);
|
}
|
throwError(errMsg);
|
}
|
|
const parser = parserName ? getRawValueParser(parserName) : null;
|
if (parserName && !parser) {
|
if (__DEV__) {
|
errMsg = makePrintable(
|
'Invalid parser name ' + parserName + '.\n',
|
'Illegal config:', orderExpr, '.\n'
|
);
|
}
|
throwError(errMsg);
|
}
|
|
orderDefList.push({
|
dimIdx: dimInfo.index,
|
parser: parser,
|
comparator: new SortOrderComparator(order, incomparable)
|
});
|
});
|
|
// TODO: support it?
|
const sourceFormat = upstream.sourceFormat;
|
if (sourceFormat !== SOURCE_FORMAT_ARRAY_ROWS
|
&& sourceFormat !== SOURCE_FORMAT_OBJECT_ROWS
|
) {
|
if (__DEV__) {
|
errMsg = 'sourceFormat "' + sourceFormat + '" is not supported yet';
|
}
|
throwError(errMsg);
|
}
|
|
// Other upstream format are all array.
|
const resultData = [];
|
for (let i = 0, len = upstream.count(); i < len; i++) {
|
resultData.push(upstream.getRawDataItem(i));
|
}
|
|
resultData.sort(function (item0, item1) {
|
for (let i = 0; i < orderDefList.length; i++) {
|
const orderDef = orderDefList[i];
|
let val0 = upstream.retrieveValueFromItem(item0, orderDef.dimIdx);
|
let val1 = upstream.retrieveValueFromItem(item1, orderDef.dimIdx);
|
if (orderDef.parser) {
|
val0 = orderDef.parser(val0) as OptionDataValue;
|
val1 = orderDef.parser(val1) as OptionDataValue;
|
}
|
const result = orderDef.comparator.evaluate(val0, val1);
|
if (result !== 0) {
|
return result;
|
}
|
}
|
return 0;
|
});
|
|
return {
|
data: resultData as ExternalDataTransformResultItem['data']
|
};
|
}
|
};
|