Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
半栈学徒
incubator-echarts
提交
81d1306b
I
incubator-echarts
项目概览
半栈学徒
/
incubator-echarts
与 Fork 源项目一致
从无法访问的项目Fork
通知
5
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
I
incubator-echarts
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
81d1306b
编写于
8月 14, 2020
作者:
1
100pah
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix: [data-transform] (1) clarity the detail of value comparison. (2) rename "parse" to "parser".
上级
ff906a44
变更
6
展开全部
隐藏空白更改
内联
并排
Showing
6 changed file
with
662 addition
and
354 deletion
+662
-354
src/component/transform/sortTransform.ts
src/component/transform/sortTransform.ts
+31
-28
src/data/helper/dataValueHelper.ts
src/data/helper/dataValueHelper.ts
+111
-95
src/util/conditionalExpression.ts
src/util/conditionalExpression.ts
+14
-18
src/util/number.ts
src/util/number.ts
+6
-9
test/data-transform.html
test/data-transform.html
+142
-31
test/ut/spec/data/dataValueHelper.test.js
test/ut/spec/data/dataValueHelper.test.js
+358
-173
未找到文件。
src/component/transform/sortTransform.ts
浏览文件 @
81d1306b
...
...
@@ -22,10 +22,10 @@ import {
DimensionLoose
,
SOURCE_FORMAT_KEYED_COLUMNS
,
DimensionIndex
,
OptionDataValue
}
from
'
../../util/types
'
;
import
{
makePrintable
,
throwError
}
from
'
../../util/log
'
;
import
{
isArray
,
each
,
hasOwn
}
from
'
zrender/src/core/util
'
;
import
{
isArray
,
each
}
from
'
zrender/src/core/util
'
;
import
{
normalizeToArray
}
from
'
../../util/model
'
;
import
{
RawValueParserType
,
getRawValueParser
,
createRelational
Comparator
RawValueParserType
,
getRawValueParser
,
SortOrder
Comparator
}
from
'
../../data/helper/dataValueHelper
'
;
/**
...
...
@@ -54,12 +54,13 @@ export interface SortTransformOption extends DataTransformOption {
// PENDING: whether support { dimension: 'score', order: 'asc' } ?
type
OrderExpression
=
{
dimension
:
DimensionLoose
;
order
:
SortOrder
;
parse
?:
RawValueParserType
;
order
:
'
asc
'
|
'
desc
'
;
parser
?:
RawValueParserType
;
// Value that is not comparable (like null/undefined) will be
// put to head or tail.
incomparable
?:
'
min
'
|
'
max
'
;
};
type
SortOrder
=
'
asc
'
|
'
desc
'
;
const
SortOrderValidMap
=
{
asc
:
true
,
desc
:
true
}
as
const
;
let
sampleLog
=
''
;
if
(
__DEV__
)
{
...
...
@@ -95,13 +96,14 @@ export const sortTransform: ExternalDataTransform<SortTransformOption> = {
const
orderDefList
:
{
dimIdx
:
DimensionIndex
;
orderReturn
:
-
1
|
1
;
parser
:
ReturnType
<
typeof
getRawValueParser
>
;
comparator
:
SortOrderComparator
}[]
=
[];
each
(
orderExprList
,
function
(
orderExpr
)
{
const
dimLoose
=
orderExpr
.
dimension
;
const
order
=
orderExpr
.
order
;
const
parserName
=
orderExpr
.
parse
;
const
parserName
=
orderExpr
.
parser
;
const
incomparable
=
orderExpr
.
incomparable
;
if
(
dimLoose
==
null
)
{
if
(
__DEV__
)
{
...
...
@@ -110,13 +112,28 @@ export const sortTransform: ExternalDataTransform<SortTransformOption> = {
throwError
(
errMsg
);
}
if
(
!
hasOwn
(
SortOrderValidMap
,
order
)
)
{
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
=
source
.
getDimensionInfo
(
dimLoose
);
if
(
!
dimInfo
)
{
if
(
__DEV__
)
{
...
...
@@ -142,8 +159,8 @@ export const sortTransform: ExternalDataTransform<SortTransformOption> = {
orderDefList
.
push
({
dimIdx
:
dimInfo
.
index
,
orderReturn
:
order
===
'
asc
'
?
-
1
:
1
,
parser
:
parser
parser
:
parser
,
comparator
:
new
SortOrderComparator
(
order
,
incomparable
)
});
});
...
...
@@ -170,9 +187,6 @@ export const sortTransform: ExternalDataTransform<SortTransformOption> = {
resultData
.
push
(
source
.
getRawDataItem
(
i
));
}
const
lt
=
createRelationalComparator
(
'
lt
'
);
const
gt
=
createRelationalComparator
(
'
gt
'
);
resultData
.
sort
(
function
(
item0
,
item1
)
{
if
(
item0
===
headerPlaceholder
)
{
return
-
1
;
...
...
@@ -180,15 +194,6 @@ export const sortTransform: ExternalDataTransform<SortTransformOption> = {
if
(
item1
===
headerPlaceholder
)
{
return
1
;
}
// FIXME: check other empty?
// Always put empty item last?
if
(
item0
==
null
)
{
return
1
;
}
if
(
item1
==
null
)
{
return
-
1
;
}
// TODO Optimize a little: manually loop unrolling?
for
(
let
i
=
0
;
i
<
orderDefList
.
length
;
i
++
)
{
const
orderDef
=
orderDefList
[
i
];
let
val0
=
source
.
retrieveItemValue
(
item0
,
orderDef
.
dimIdx
);
...
...
@@ -197,11 +202,9 @@ export const sortTransform: ExternalDataTransform<SortTransformOption> = {
val0
=
orderDef
.
parser
(
val0
)
as
OptionDataValue
;
val1
=
orderDef
.
parser
(
val1
)
as
OptionDataValue
;
}
if
(
lt
.
evaluate
(
val0
,
val1
))
{
return
orderDef
.
orderReturn
;
}
else
if
(
gt
.
evaluate
(
val0
,
val1
))
{
return
-
orderDef
.
orderReturn
;
const
result
=
orderDef
.
comparator
.
evaluate
(
val0
,
val1
);
if
(
result
!==
0
)
{
return
result
;
}
}
return
0
;
...
...
src/data/helper/dataValueHelper.ts
浏览文件 @
81d1306b
...
...
@@ -21,6 +21,7 @@ import { ParsedValue, DimensionType } from '../../util/types';
import
OrdinalMeta
from
'
../OrdinalMeta
'
;
import
{
parseDate
,
numericToNumber
}
from
'
../../util/number
'
;
import
{
createHashMap
,
trim
,
hasOwn
}
from
'
zrender/src/core/util
'
;
import
{
throwError
}
from
'
../../util/log
'
;
/**
...
...
@@ -99,51 +100,99 @@ export function getRawValueParser(type: RawValueParserType): RawValueParser {
export
interface
UnaryExpression
{
evaluate
(
val
:
unknown
):
unknown
;
}
export
interface
BinaryExpression
{
evaluate
(
lval
:
unknown
,
rval
:
unknown
):
unknown
;
export
interface
FilterComparator
{
evaluate
(
val
:
unknown
):
boolean
;
}
class
OrderComparatorUnary
implements
UnaryExpression
{
_rval
:
unknown
;
_rvalTypeof
:
string
;
// typeof rval
_rvalFloat
:
number
;
_rvalIsNumeric
:
boolean
;
_opFn
:
(
lval
:
unknown
,
rval
:
unknown
)
=>
boolean
;
const
ORDER_COMPARISON_OP_MAP
:
{
[
key
in
OrderRelationOperator
]:
((
lval
:
unknown
,
rval
:
unknown
)
=>
boolean
)
}
=
{
lt
:
(
lval
,
rval
)
=>
lval
<
rval
,
lte
:
(
lval
,
rval
)
=>
lval
<=
rval
,
gt
:
(
lval
,
rval
)
=>
lval
>
rval
,
gte
:
(
lval
,
rval
)
=>
lval
>=
rval
};
class
FilterOrderComparator
implements
FilterComparator
{
private
_rvalFloat
:
number
;
private
_opFn
:
(
lval
:
unknown
,
rval
:
unknown
)
=>
boolean
;
constructor
(
op
:
OrderRelationOperator
,
rval
:
unknown
)
{
if
(
typeof
rval
!==
'
number
'
)
{
let
errMsg
=
''
;
if
(
__DEV__
)
{
errMsg
=
'
rvalue of "<", ">", "<=", ">=" can only be number in filter.
'
;
}
throwError
(
errMsg
);
}
this
.
_opFn
=
ORDER_COMPARISON_OP_MAP
[
op
];
this
.
_rvalFloat
=
numericToNumber
(
rval
);
}
// Performance sensitive.
evaluate
(
lval
:
unknown
):
boolean
{
// Most cases is 'number', and typeof maybe 10 times faseter than parseFloat.
const
lvalIsNumber
=
typeof
lval
===
'
number
'
;
return
(
lvalIsNumber
&&
this
.
_rvalIsNumeric
)
return
typeof
lval
===
'
number
'
?
this
.
_opFn
(
lval
,
this
.
_rvalFloat
)
:
(
lvalIsNumber
||
this
.
_rvalTypeof
===
'
number
'
)
?
this
.
_opFn
(
numericToNumber
(
lval
),
this
.
_rvalFloat
)
:
false
;
:
this
.
_opFn
(
numericToNumber
(
lval
),
this
.
_rvalFloat
);
}
}
class
OrderComparatorBinary
implements
BinaryExpression
{
_opFn
:
(
lval
:
unknown
,
rval
:
unknown
)
=>
boolean
;
export
class
SortOrderComparator
{
private
_incomparable
:
number
;
private
_resultLT
:
-
1
|
1
;
/**
* @param order by defualt: 'asc'
* @param incomparable by defualt: Always on the tail.
* That is, if 'asc' => 'max', if 'desc' => 'min'
*/
constructor
(
order
:
'
asc
'
|
'
desc
'
,
incomparable
:
'
min
'
|
'
max
'
)
{
const
isDesc
=
order
===
'
desc
'
;
this
.
_resultLT
=
isDesc
?
1
:
-
1
;
if
(
incomparable
==
null
)
{
incomparable
=
isDesc
?
'
min
'
:
'
max
'
;
}
this
.
_incomparable
=
incomparable
===
'
min
'
?
-
Infinity
:
Infinity
;
}
// Performance sensitive.
evaluate
(
lval
:
unknown
,
rval
:
unknown
):
boolean
{
evaluate
(
lval
:
unknown
,
rval
:
unknown
):
-
1
|
0
|
1
{
// Most cases is 'number', and typeof maybe 10 times faseter than parseFloat.
const
lvalIsNumber
=
typeof
lval
===
'
number
'
;
const
rvalIsNumber
=
typeof
rval
===
'
number
'
;
return
(
lvalIsNumber
&&
rvalIsNumber
)
?
this
.
_opFn
(
lval
,
rval
)
:
(
lvalIsNumber
||
rvalIsNumber
)
?
this
.
_opFn
(
numericToNumber
(
lval
),
numericToNumber
(
rval
))
:
false
;
const
lvalTypeof
=
typeof
lval
;
const
rvalTypeof
=
typeof
rval
;
let
lvalFloat
=
lvalTypeof
===
'
number
'
?
lval
:
numericToNumber
(
lval
);
let
rvalFloat
=
rvalTypeof
===
'
number
'
?
rval
:
numericToNumber
(
rval
);
const
lvalIncmpr
=
isNaN
(
lvalFloat
as
number
);
const
rvalIncmpr
=
isNaN
(
rvalFloat
as
number
);
if
(
lvalIncmpr
)
{
lvalFloat
=
this
.
_incomparable
;
}
if
(
rvalIncmpr
)
{
rvalFloat
=
this
.
_incomparable
;
}
// In most cases, pure string sort has no meanings. But it can exists when need to
// group two categories (and order by anthor dimension meanwhile).
// But if we support string sort, we still need to avoid the misleading of `'2' > '12'`,
// and support '-' means empty, and trade `'abc' > 2` as incomparable.
// So we support string comparison only if both lval and rval are string and not numeric.
if
(
lvalIncmpr
&&
rvalIncmpr
&&
lvalTypeof
===
'
string
'
&&
rvalTypeof
===
'
string
'
)
{
lvalFloat
=
lval
;
rvalFloat
=
rval
;
}
return
lvalFloat
<
rvalFloat
?
this
.
_resultLT
:
lvalFloat
>
rvalFloat
?
(
-
this
.
_resultLT
as
-
1
|
1
)
:
0
;
}
}
class
EqualityComparatorUnary
implements
UnaryExpression
{
_rval
:
unknown
;
_rvalTypeof
:
string
;
// typeof rval
_rvalFloat
:
number
;
_rvalIsNumeric
:
boolean
;
_isEq
:
boolean
;
class
FilterEqualityComparator
implements
FilterComparator
{
private
_isEQ
:
boolean
;
private
_rval
:
unknown
;
private
_rvalTypeof
:
string
;
private
_rvalFloat
:
number
;
constructor
(
isEq
:
boolean
,
rval
:
unknown
)
{
this
.
_rval
=
rval
;
this
.
_isEQ
=
isEq
;
this
.
_rvalTypeof
=
typeof
rval
;
this
.
_rvalFloat
=
numericToNumber
(
rval
);
}
// Performance sensitive.
evaluate
(
lval
:
unknown
):
boolean
{
let
eqResult
=
lval
===
this
.
_rval
;
...
...
@@ -153,80 +202,47 @@ class EqualityComparatorUnary implements UnaryExpression {
eqResult
=
numericToNumber
(
lval
)
===
this
.
_rvalFloat
;
}
}
return
this
.
_isE
q
?
eqResult
:
!
eqResult
;
return
this
.
_isE
Q
?
eqResult
:
!
eqResult
;
}
}
class
EqualityComparatorBinary
implements
BinaryExpression
{
_isEq
:
boolean
;
// Performance sensitive.
evaluate
(
lval
:
unknown
,
rval
:
unknown
):
boolean
{
let
eqResult
=
lval
===
rval
;
if
(
!
eqResult
)
{
const
lvalTypeof
=
typeof
lval
;
const
rvalTypeof
=
typeof
rval
;
if
(
lvalTypeof
!==
rvalTypeof
&&
(
lvalTypeof
===
'
number
'
||
rvalTypeof
===
'
number
'
))
{
eqResult
=
numericToNumber
(
lval
)
===
numericToNumber
(
rval
);
}
}
return
this
.
_isEq
?
eqResult
:
!
eqResult
;
}
}
const
ORDER_COMPARISON_OP_MAP
=
{
lt
:
(
tarVal
:
unknown
,
condVal
:
unknown
)
=>
tarVal
<
condVal
,
lte
:
(
tarVal
:
unknown
,
condVal
:
unknown
)
=>
tarVal
<=
condVal
,
gt
:
(
tarVal
:
unknown
,
condVal
:
unknown
)
=>
tarVal
>
condVal
,
gte
:
(
tarVal
:
unknown
,
condVal
:
unknown
)
=>
tarVal
>=
condVal
}
as
const
;
export
type
RelationalOperator
=
'
lt
'
|
'
lte
'
|
'
gt
'
|
'
gte
'
|
'
eq
'
|
'
ne
'
;
type
OrderRelationOperator
=
'
lt
'
|
'
lte
'
|
'
gt
'
|
'
gte
'
;
export
type
RelationalOperator
=
OrderRelationOperator
|
'
eq
'
|
'
ne
'
;
/**
* [COMPARISON_RULE]
* `lt`, `lte`, `gt`, `gte`:
* + If two "number" or a "number" and a "numeric": convert to number and compare.
* + Else return `false`.
* [FILTER_COMPARISON_RULE]
* `lt`|`lte`|`gt`|`gte`:
* + rval must be a number. And lval will be converted to number (`numericToNumber`) to compare.
* `eq`:
* + If same type, compare with
===
.
* + If t
wo "number" or a "number" and a "numeric": convert to number and
compare.
* + If same type, compare with
`===`
.
* + If t
here is one number, convert to number (`numericToNumber`) to
compare.
* + Else return `false`.
* `ne`:
* + Not `eq`.
*
* Definition of "numeric": see `util/number.ts#numericToNumber`.
* [SORT_COMPARISON_RULE]
* Only `lt`|`gt`.
* Always convert to number (`numericToNumer`) to compare.
* (e.g., consider case: [12, " 13 ", " 14 ", null, 15])
*
* [CHECK_LIST_OF_THE_RULE_DESIGN]
* + Do not support string comparison until required. And also need to
* void the misleading of "2" > "12".
* + Should avoid the misleading case:
* `" 22 " gte "22"` is `true` but `" 22 " eq "22"` is `false`.
* + JS bad case should be avoided: null <= 0, [] <= 0, ' ' <= 0, ...
* + Only "numeric" can be converted to comparable number, otherwise converted to NaN.
* See `util/number.ts#numericToNumber`.
*
* [MEMO]
* + Do not support string comparison until required. And also need to consider the
* misleading of "2" > "12".
* + JS bad case considered: null <= 0, [] <= 0, ' ' <= 0, ...
* @return If `op` is not `RelationalOperator`, return null;
*/
export
function
createRelationalComparator
(
op
:
RelationalOperator
):
BinaryExpression
;
export
function
createRelationalComparator
(
op
:
RelationalOperator
,
isUnary
:
true
,
rval
:
unknown
):
UnaryExpression
;
export
function
createRelationalComparator
(
op
:
RelationalOperator
,
isUnary
?:
true
,
export
function
createFilterComparator
(
op
:
string
,
rval
?:
unknown
):
UnaryExpression
|
BinaryExpression
{
let
comparator
;
if
(
op
===
'
eq
'
||
op
===
'
ne
'
)
{
comparator
=
isUnary
?
new
EqualityComparatorUnary
()
:
new
EqualityComparatorBinary
();
comparator
.
_isEq
=
op
===
'
eq
'
;
}
else
{
comparator
=
isUnary
?
new
OrderComparatorUnary
()
:
new
OrderComparatorBinary
();
comparator
.
_opFn
=
ORDER_COMPARISON_OP_MAP
[
op
];
}
if
(
isUnary
)
{
const
unaryComp
=
comparator
as
OrderComparatorUnary
|
EqualityComparatorUnary
;
unaryComp
.
_rval
=
rval
;
unaryComp
.
_rvalTypeof
=
typeof
rval
;
const
rvalFloat
=
unaryComp
.
_rvalFloat
=
numericToNumber
(
rval
);
unaryComp
.
_rvalIsNumeric
=
!
isNaN
(
rvalFloat
);
// eslint-disable-line eqeqeq
}
return
comparator
;
}
export
function
isRelationalOperator
(
op
:
string
):
op
is
RelationalOperator
{
return
hasOwn
(
ORDER_COMPARISON_OP_MAP
,
op
)
||
op
===
'
eq
'
||
op
===
'
ne
'
;
):
FilterComparator
{
return
(
op
===
'
eq
'
||
op
===
'
ne
'
)
?
new
FilterEqualityComparator
(
op
===
'
eq
'
,
rval
)
:
hasOwn
(
ORDER_COMPARISON_OP_MAP
,
op
)
?
new
FilterOrderComparator
(
op
as
OrderRelationOperator
,
rval
)
:
null
;
}
src/util/conditionalExpression.ts
浏览文件 @
81d1306b
...
...
@@ -23,13 +23,13 @@ import {
}
from
'
zrender/src/core/util
'
;
import
{
throwError
,
makePrintable
}
from
'
./log
'
;
import
{
RawValueParserType
,
getRawValueParser
,
isRelationalOperator
,
createRelationalComparator
,
RelationalOperator
,
UnaryExpression
RawValueParserType
,
getRawValueParser
,
RelationalOperator
,
FilterComparator
,
createFilterComparator
}
from
'
../data/helper/dataValueHelper
'
;
// PENDING:
// (1) Support more parser like: `parse
: 'trim'`, `parse: 'lowerCase'`, `parse: 'year'`, `parse
: 'dayOfWeek'`?
// (1) Support more parser like: `parse
r: 'trim'`, `parser: 'lowerCase'`, `parser: 'year'`, `parser
: 'dayOfWeek'`?
// (2) Support piped parser ?
// (3) Support callback parser or callback condition?
// (4) At present do not support string expression yet but only stuctured expression.
...
...
@@ -77,24 +77,24 @@ import {
* ```js
* // Trim if string
* {
* parse: 'trim',
* parse
r
: 'trim',
* eq: 'Flowers'
* }
* // Parse as time and enable arithmetic relation comparison.
* {
* parse: 'time',
* parse
r
: 'time',
* lt: '2012-12-12'
* }
* // Normalize number-like string and make '-' to Null.
* {
* parse: 'time',
* parse
r
: 'time',
* lt: '2012-12-12'
* }
* // Normalize to number:
* // + number-like string (like ' 123 ') can be converted to a number.
* // + where null/undefined or other string will be converted to NaN.
* {
* parse: 'number',
* parse
r
: 'number',
* eq: 2011
* }
* // RegExp, include the feature in SQL: `like '%xxx%'`.
...
...
@@ -164,13 +164,13 @@ type RelationalExpressionOptionByOpAlias = Record<keyof typeof RELATIONAL_EXPRES
interface
RelationalExpressionOption
extends
RelationalExpressionOptionByOp
,
RelationalExpressionOptionByOpAlias
{
dimension
?:
DimensionLoose
;
parse
?:
RawValueParserType
;
parse
r
?:
RawValueParserType
;
}
type
RelationalExpressionOpEvaluate
=
(
tarVal
:
unknown
,
condVal
:
unknown
)
=>
boolean
;
class
RegExpEvaluator
implements
UnaryExpression
{
class
RegExpEvaluator
implements
FilterComparator
{
private
_condVal
:
RegExp
;
constructor
(
rVal
:
unknown
)
{
...
...
@@ -279,7 +279,7 @@ class RelationalConditionInternal implements ParsedConditionInternal {
valueParser
:
ReturnType
<
typeof
getRawValueParser
>
;
// If no parser, be null/undefined.
getValue
:
ConditionalExpressionValueGetter
;
subCondList
:
UnaryExpression
[];
subCondList
:
FilterComparator
[];
evaluate
()
{
const
needParse
=
!!
this
.
valueParser
;
...
...
@@ -392,12 +392,12 @@ function parseRelationalOption(
const
subCondList
=
[]
as
RelationalConditionInternal
[
'
subCondList
'
];
const
exprKeys
=
keys
(
exprOption
);
const
parserName
=
exprOption
.
parse
;
const
parserName
=
exprOption
.
parse
r
;
const
valueParser
=
parserName
?
getRawValueParser
(
parserName
)
:
null
;
for
(
let
i
=
0
;
i
<
exprKeys
.
length
;
i
++
)
{
const
keyRaw
=
exprKeys
[
i
];
if
(
keyRaw
===
'
parse
'
||
getters
.
valueGetterAttrMap
.
get
(
keyRaw
))
{
if
(
keyRaw
===
'
parse
r
'
||
getters
.
valueGetterAttrMap
.
get
(
keyRaw
))
{
continue
;
}
...
...
@@ -406,12 +406,8 @@ function parseRelationalOption(
:
(
keyRaw
as
keyof
RelationalExpressionOptionByOp
);
const
condValueRaw
=
exprOption
[
keyRaw
];
const
condValueParsed
=
valueParser
?
valueParser
(
condValueRaw
)
:
condValueRaw
;
const
evaluator
=
isRelationalOperator
(
op
)
?
createRelationalComparator
(
op
,
true
,
condValueParsed
)
:
op
===
'
reg
'
?
new
RegExpEvaluator
(
condValueParsed
)
:
null
;
const
evaluator
=
createFilterComparator
(
op
,
condValueParsed
)
||
(
op
===
'
reg
'
&&
new
RegExpEvaluator
(
condValueParsed
));
if
(
!
evaluator
)
{
if
(
__DEV__
)
{
...
...
src/util/number.ts
浏览文件 @
81d1306b
...
...
@@ -549,22 +549,19 @@ export function reformIntervals(list: IntervalItem[]): IntervalItem[] {
* non-string, ...
*
* @test See full test cases in `test/ut/spec/util/number.js`.
* @return Must be a typeof number. If not numeric, return NaN.
*/
export
function
numericToNumber
(
val
:
unknown
):
number
{
const
valFloat
=
parseFloat
(
val
as
string
);
return
isNumericHavingParseFloat
(
val
,
valFloat
)
?
valFloat
:
NaN
;
return
(
valFloat
==
val
// eslint-disable-line eqeqeq
&&
(
valFloat
!==
0
||
typeof
val
!==
'
string
'
||
val
.
indexOf
(
'
x
'
)
<=
0
)
// For case ' 0x0 '.
)
?
valFloat
:
NaN
;
}
/**
* Definition of "numeric": see `numericToNumber`.
*/
export
function
isNumeric
(
val
:
unknown
):
val
is
number
{
return
isNumericHavingParseFloat
(
val
,
parseFloat
(
val
as
string
));
}
function
isNumericHavingParseFloat
(
val
:
unknown
,
valFloat
:
number
):
val
is
number
{
return
(
valFloat
==
val
// eslint-disable-line eqeqeq
&&
(
valFloat
!==
0
||
typeof
val
!==
'
string
'
||
val
.
indexOf
(
'
x
'
)
<=
0
)
// For case ' 0x0 '.
);
return
!
isNaN
(
numericToNumber
(
val
));
}
test/data-transform.html
浏览文件 @
81d1306b
...
...
@@ -80,21 +80,24 @@ under the License.
Age
:
1
,
Sex
:
2
,
Score
:
3
,
Date
:
4
Date
:
4
,
DirtyNumber
:
5
,
Numeric
:
6
,
HasEmpty
:
7
};
var
NAME_SCORE_DIRTY_DATA_HEADER
=
[
'
Name
'
,
'
Age
'
,
'
Sex
'
,
'
Score
'
,
'
Date
'
];
[
'
Name
'
,
'
Age
'
,
'
Sex
'
,
'
Score
'
,
'
Date
'
,
'
DirtyNumber
'
,
'
Numeric
'
,
'
HasEmpty
'
];
var
NAME_SCORE_DIRTY_DATA_NO_HEADER
=
[
// This is for trim testing.
[
'
Jobs Mat
'
,
41
,
'
male
'
,
314
,
'
2011-02-12
'
],
[
'
Jobs Mat
'
,
41
,
'
male
'
,
314
,
'
2011-02-12
'
,
'
13
'
,
'
91000
'
,
45
],
// This is for edge testing (03-01, 20)
[
'
Hottlyuipe Xu
'
,
20
,
'
female
'
,
351
,
'
2011-03-01
'
],
[
'
Jone Mat
'
,
52
,
'
male
'
,
287
,
'
2011-02-14
'
],
[
'
Uty Xu
'
,
19
,
'
male
'
,
219
,
'
2011-02-18
'
],
[
'
Tatum von Godden
'
,
25
,
'
female
'
,
301
,
'
2011-04-02
'
],
[
'
Must Godden
'
,
31
,
'
female
'
,
235
,
'
2011-03-19
'
],
[
'
Caoas Xu
'
,
71
,
'
male
'
,
318
,
'
2011-02-24
'
],
[
'
Malise Mat
'
,
67
,
'
female
'
,
366
,
'
2011-03-12
'
],
[
'
Hottlyuipe Xu
'
,
20
,
'
female
'
,
351
,
'
2011-03-01
'
,
44
,
'
83000
'
,
13
],
[
'
Jone Mat
'
,
52
,
'
male
'
,
287
,
'
2011-02-14
'
,
null
,
'
43000
'
,
null
],
[
'
Uty Xu
'
,
19
,
'
male
'
,
219
,
'
2011-02-18
'
,
undefined
,
'
63000
'
,
81
],
[
'
Tatum von Godden
'
,
25
,
'
female
'
,
301
,
'
2011-04-02
'
,
'
-
'
,
'
13000
'
,
undefined
],
[
'
Must Godden
'
,
31
,
'
female
'
,
235
,
'
2011-03-19
'
,
'
454
'
,
'
-
'
,
32
],
[
'
Caoas Xu
'
,
71
,
'
male
'
,
318
,
'
2011-02-24
'
,
NaN
,
'
73000
'
,
'
-
'
],
[
'
Malise Mat
'
,
67
,
'
female
'
,
366
,
'
2011-03-12
'
,
'
232a
'
,
'
23000
'
,
19
]
];
var
NAME_SCORE_DIRTY_DATA_WITH_HEADER
=
[
NAME_SCORE_DIRTY_DATA_HEADER
]
...
...
@@ -287,7 +290,8 @@ under the License.
NAME_SCORE_DIM
.
Date
,
NAME_SCORE_DIM
.
Score
,
NAME_SCORE_DIM
.
Sex
,
NAME_SCORE_DIM
.
Age
NAME_SCORE_DIM
.
Age
,
NAME_SCORE_DIM
.
DirtyNumber
]
};
option
.
series
.
push
(
series
);
...
...
@@ -299,7 +303,7 @@ under the License.
transform
:
{
type
:
'
filter
'
,
// print: true,
config
:
{
dimension
:
NAME_SCORE_DIM
.
Name
,
eq
:
'
Jobs Mat
'
,
parse
:
'
trim
'
}
config
:
{
dimension
:
NAME_SCORE_DIM
.
Name
,
eq
:
'
Jobs Mat
'
,
parse
r
:
'
trim
'
}
}
});
addCartesian
({
...
...
@@ -314,7 +318,7 @@ under the License.
transform
:
{
type
:
'
filter
'
,
// print: true,
config
:
{
dimension
:
NAME_SCORE_DIM
.
Date
,
lt
:
'
2011-03
'
,
gte
:
'
2011-02
'
,
parse
:
'
time
'
}
config
:
{
dimension
:
NAME_SCORE_DIM
.
Date
,
lt
:
'
2011-03
'
,
gte
:
'
2011-02
'
,
parse
r
:
'
time
'
}
}
});
addCartesian
({
...
...
@@ -329,7 +333,7 @@ under the License.
transform
:
{
type
:
'
filter
'
,
// print: true,
config
:
{
dimension
:
NAME_SCORE_DIM
.
Date
,
lte
:
'
2011-03
'
,
gte
:
'
2011-02-29
'
,
parse
:
'
time
'
}
config
:
{
dimension
:
NAME_SCORE_DIM
.
Date
,
lte
:
'
2011-03
'
,
gte
:
'
2011-02-29
'
,
parse
r
:
'
time
'
}
}
});
addCartesian
({
...
...
@@ -344,7 +348,7 @@ under the License.
transform
:
{
type
:
'
filter
'
,
// print: true,
config
:
{
dimension
:
NAME_SCORE_DIM
.
Name
,
reg
:
/
\s
Xu$/
,
parse
:
'
trim
'
}
config
:
{
dimension
:
NAME_SCORE_DIM
.
Name
,
reg
:
/
\s
Xu$/
,
parse
r
:
'
trim
'
}
}
});
addCartesian
({
...
...
@@ -359,7 +363,7 @@ under the License.
transform
:
{
type
:
'
filter
'
,
// print: true,
config
:
{
dimension
:
NAME_SCORE_DIM
.
Sex
,
ne
:
'
male
'
,
parse
:
'
trim
'
}
config
:
{
dimension
:
NAME_SCORE_DIM
.
Sex
,
ne
:
'
male
'
,
parse
r
:
'
trim
'
}
}
});
addCartesian
({
...
...
@@ -377,7 +381,7 @@ under the License.
// print: true,
config
:
{
and
:
[
{
dimension
:
NAME_SCORE_DIM
.
Sex
,
eq
:
'
male
'
,
parse
:
'
trim
'
},
{
dimension
:
NAME_SCORE_DIM
.
Sex
,
eq
:
'
male
'
,
parse
r
:
'
trim
'
},
{
dimension
:
NAME_SCORE_DIM
.
Score
,
'
>
'
:
300
}
]
}
...
...
@@ -464,6 +468,30 @@ under the License.
});
option
.
dataset
.
push
({
id
:
'
j
'
,
transform
:
{
type
:
'
filter
'
,
// print: true,
config
:
{
or
:
[{
dimension
:
NAME_SCORE_DIM
.
DirtyNumber
,
eq
:
454
},
{
dimension
:
NAME_SCORE_DIM
.
DirtyNumber
,
eq
:
232
}]
}
}
});
addCartesian
({
series
:
{
datasetId
:
'
j
'
,
encode
:
{
label
:
[
NAME_SCORE_DIM
.
DirtyNumber
]
}
},
xAxis
:
{
name
:
'
Show only "Must Godden"
'
}
});
var
chart
=
testHelper
.
create
(
echarts
,
'
main_cartesian_parse_trim_time_reg
'
,
{
...
...
@@ -501,7 +529,7 @@ under the License.
var
leftStart
=
50
;
var
leftBase
=
leftStart
;
var
topBase
=
30
;
var
gridWidth
=
1
00
;
var
gridWidth
=
2
00
;
var
gridHeight
=
100
;
var
gapWidth
=
70
;
var
gapHeight
=
80
;
...
...
@@ -537,7 +565,13 @@ under the License.
series
.
type
=
'
bar
'
;
series
.
xAxisIndex
=
option
.
xAxis
.
length
-
1
;
series
.
yAxisIndex
=
option
.
yAxis
.
length
-
1
;
series
.
label
=
{
show
:
true
,
position
:
'
top
'
};
series
.
label
=
{
show
:
true
,
position
:
'
insideBottom
'
,
rotate
:
90
,
align
:
'
left
'
,
verticalAlign
:
'
middle
'
};
series
.
encode
=
{
x
:
NAME_SCORE_DIM
.
Date
,
y
:
NAME_SCORE_DIM
.
Score
,
...
...
@@ -547,7 +581,10 @@ under the License.
NAME_SCORE_DIM
.
Date
,
NAME_SCORE_DIM
.
Score
,
NAME_SCORE_DIM
.
Sex
,
NAME_SCORE_DIM
.
Age
NAME_SCORE_DIM
.
Age
,
NAME_SCORE_DIM
.
DirtyNumber
,
NAME_SCORE_DIM
.
Numeric
,
NAME_SCORE_DIM
.
HasEmpty
]
};
option
.
series
.
push
(
series
);
...
...
@@ -564,9 +601,10 @@ under the License.
});
addCartesian
({
series
:
{
encode
:
{
label
:
NAME_SCORE_DIM
.
Score
},
datasetId
:
'
a
'
},
xAxis
:
{
name
:
'
Show all eight
\n
order by Score asc
'
}
xAxis
:
{
name
:
'
Show all eight
bars
\n
order by Score asc
'
}
});
option
.
dataset
.
push
({
...
...
@@ -582,7 +620,7 @@ under the License.
datasetId
:
'
b
'
,
encode
:
{
label
:
NAME_SCORE_DIM
.
Age
}
},
xAxis
:
{
name
:
'
Show all eight
\n
order by Age desc
'
}
xAxis
:
{
name
:
'
Show all eight
bars
\n
order by Age desc
'
}
});
option
.
dataset
.
push
({
...
...
@@ -599,9 +637,9 @@ under the License.
addCartesian
({
series
:
{
datasetId
:
'
c
'
,
encode
:
{
label
:
NAME_SCORE_DIM
.
Sex
}
encode
:
{
label
:
[
NAME_SCORE_DIM
.
Sex
,
NAME_SCORE_DIM
.
Score
]
}
},
xAxis
:
{
name
:
'
Show all eight
\n
Sex asc, Score desc
'
}
xAxis
:
{
name
:
'
Show all eight
bars
\n
Sex asc (all female left)
\n
Score desc in each Sex
'
}
});
option
.
dataset
.
push
({
...
...
@@ -610,15 +648,16 @@ under the License.
type
:
'
sort
'
,
// print: true,
config
:
[
{
dimension
:
NAME_SCORE_DIM
.
Date
,
order
:
'
asc
'
,
parse
:
'
time
'
}
{
dimension
:
NAME_SCORE_DIM
.
Date
,
order
:
'
asc
'
,
parse
r
:
'
time
'
}
]
}
});
addCartesian
({
series
:
{
encode
:
{
label
:
NAME_SCORE_DIM
.
Date
},
datasetId
:
'
d
'
},
xAxis
:
{
name
:
'
Show all eight
\n
Date asc
'
}
xAxis
:
{
name
:
'
Show all eight
bars
\n
Date asc
'
}
});
...
...
@@ -626,26 +665,98 @@ under the License.
id
:
'
e
'
,
transform
:
[{
type
:
'
filter
'
,
// print: true,
config
:
{
dimension
:
NAME_SCORE_DIM
.
Age
,
lte
:
40
,
gte
:
20
}
},
{
type
:
'
sort
'
,
// print: true,
config
:
{
dimension
:
NAME_SCORE_DIM
.
Score
,
order
:
'
asc
'
}
}]
});
addCartesian
({
series
:
{
encode
:
{
label
:
[
NAME_SCORE_DIM
.
Age
,
NAME_SCORE_DIM
.
Score
]
},
datasetId
:
'
e
'
},
xAxis
:
{
name
:
'
Show three ponits
\n
Filter by Age 20-40
\n
Order by Score
'
}
xAxis
:
{
name
:
'
Show three bars
\n
Filter by Age 20-40
\n
Order by Score asc
'
}
});
option
.
dataset
.
push
({
id
:
'
f
'
,
transform
:
{
type
:
'
sort
'
,
config
:
[
{
dimension
:
NAME_SCORE_DIM
.
DirtyNumber
,
order
:
'
desc
'
,
parser
:
'
number
'
}
]
}
});
addCartesian
({
series
:
{
encode
:
{
label
:
NAME_SCORE_DIM
.
DirtyNumber
},
datasetId
:
'
f
'
},
xAxis
:
{
name
:
'
Show all eight bars
\n
Order by DirtyNumber desc
'
}
});
option
.
dataset
.
push
({
id
:
'
g
'
,
transform
:
{
type
:
'
sort
'
,
config
:
[
{
dimension
:
NAME_SCORE_DIM
.
Numeric
,
order
:
'
asc
'
}
]
}
});
addCartesian
({
series
:
{
encode
:
{
label
:
NAME_SCORE_DIM
.
Numeric
},
datasetId
:
'
g
'
},
xAxis
:
{
name
:
'
Show all eight bars
\n
Order by Numeric asc
\n
Only one empty at right
'
}
});
option
.
dataset
.
push
({
id
:
'
h
'
,
transform
:
{
type
:
'
sort
'
,
config
:
[
{
dimension
:
NAME_SCORE_DIM
.
HasEmpty
,
order
:
'
desc
'
}
]
}
});
addCartesian
({
series
:
{
encode
:
{
label
:
NAME_SCORE_DIM
.
HasEmpty
},
datasetId
:
'
h
'
},
xAxis
:
{
name
:
'
Show all eight bars
\n
Order by HasEmpty desc
\n
empty at right
'
}
});
option
.
dataset
.
push
({
id
:
'
i
'
,
transform
:
{
type
:
'
sort
'
,
config
:
[
{
dimension
:
NAME_SCORE_DIM
.
HasEmpty
,
order
:
'
desc
'
,
incomparable
:
'
max
'
}
]
}
});
addCartesian
({
series
:
{
encode
:
{
label
:
NAME_SCORE_DIM
.
HasEmpty
},
datasetId
:
'
i
'
},
xAxis
:
{
name
:
'
Show all eight bars
\n
Order by HasEmpty desc
\n
empty at left
'
}
});
var
chart
=
testHelper
.
create
(
echarts
,
'
main_cartesian_sort
'
,
{
title
:
[
'
Check each cartesians.
'
,
'
The expectationa are below each cartesian.
'
'
Test sort transform. Check each cartesians.
'
,
'
The expectationa are below each cartesian.
'
,
'
Ordered dimension is on **bar label **
'
],
width
:
chartWidth
,
height
:
600
,
...
...
test/ut/spec/data/dataValueHelper.test.js
浏览文件 @
81d1306b
此差异已折叠。
点击以展开。
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录