提交 3ad08d4e 编写于 作者: 1 100pah

fix: tweak list id name generation rule (avoid some potential flaw) and add test cases.

上级 1f71fadc
......@@ -34,7 +34,7 @@ import Element from 'zrender/src/Element';
import {
DimensionIndex, DimensionName, DimensionLoose, OptionDataItem,
ParsedValue, ParsedValueNumeric, OrdinalNumber, DimensionUserOuput,
ModelOption, SeriesDataType, OrdinalRawValue
ModelOption, SeriesDataType, OptionSourceData, SOURCE_FORMAT_TYPED_ARRAY, SOURCE_FORMAT_ORIGINAL
} from '../util/types';
import {isDataItemOption, convertOptionIdName} from '../util/model';
import { getECData } from '../util/innerStore';
......@@ -43,7 +43,8 @@ import type Graph from './Graph';
import type Tree from './Tree';
import type { VisualMeta } from '../component/visualMap/VisualMapModel';
import { parseDataValue } from './helper/dataValueHelper';
import { isSourceInstance } from './Source';
import { isSourceInstance, Source } from './Source';
import OrdinalMeta from './OrdinalMeta';
const mathFloor = Math.floor;
const isObject = zrUtil.isObject;
......@@ -160,15 +161,15 @@ export interface DataCalculationInfo<SERIES_MODEL> {
// -----------------------------
let defaultDimValueGetters: {[sourceFormat: string]: DimValueGetter};
let prepareInvertedIndex: (list: List) => void;
let getRawValueFromStore: (list: List, dimIndex: number, rawIndex: number) => ParsedValue | OrdinalRawValue;
let getIndicesCtor: (list: List) => DataArrayLikeConstructor;
let prepareStorage: (
storage: DataStorage, dimInfo: DataDimensionInfo, end: number, append?: boolean
) => void;
let getRawIndexWithoutIndices: (this: List, idx: number) => number;
let getRawIndexWithIndices: (this: List, idx: number) => number;
let getIdFromName: (list: List, name: string) => string;
let getId: (list: List, rawIndex: number) => string;
let getIdNameFromStore: (list: List, dimIdx: number, ordinalMeta: OrdinalMeta, rawIndex: number) => string;
let makeIdFromName: (list: List, idx: number) => void;
let normalizeDimensions: (dimensions: ItrParamDims) => Array<DimensionLoose>;
let validateDimensions: (list: List, dims: DimensionName[]) => void;
let cloneListForMapAndSample: (original: List, excludeDimensions: DimensionName[]) => List;
......@@ -176,6 +177,7 @@ let getInitialExtent: () => [number, number];
let setItemDataAndSeriesIndex: (this: Element, child: Element) => void;
let transferProperties: (target: List, source: List) => void;
class List<
HostModel extends Model = Model,
Visual extends DefaultDataVisual = DefaultDataVisual
......@@ -267,7 +269,10 @@ class List<
// avoid clone them too many times.
readonly userOutput: DimensionUserOuput;
// If each data item has it's own option
// Having detected that there is data item is non primitive type
// (in type `OptionDataItemObject`).
// Like `data: [ { value: xx, itemStyle: {...} }, ...]`
// At present it only happen in `SOURCE_FORMAT_ORIGINAL`.
hasItemOption: boolean = true;
// @readonly
......@@ -275,9 +280,14 @@ class List<
private _dimValueGetter: DimValueGetter;
private _dimValueGetterArrayRows: DimValueGetter;
// id or name is used on dynamic data, mapping old and new items.
// When generating id from name, avoid repeat.
private _nameRepeatCount: NameRepeatCount;
private _nameDimIdx: number;
private _nameOrdinalMeta: OrdinalMeta;
private _idDimIdx: number;
private _idOrdinalMeta: OrdinalMeta;
private _dontMakeIdFromName: boolean;
private __wrappedMethods: string[];
......@@ -318,7 +328,7 @@ class List<
dimensionInfo.coordDimIndex = 0;
}
dimensionInfo.otherDims = dimensionInfo.otherDims || {};
const otherDims = dimensionInfo.otherDims = dimensionInfo.otherDims || {};
dimensionNames.push(dimensionName);
dimensionInfos[dimensionName] = dimensionInfo;
......@@ -327,6 +337,14 @@ class List<
if (dimensionInfo.createInvertedIndices) {
invertedIndicesMap[dimensionName] = [];
}
if (otherDims.itemName === 0) {
this._nameDimIdx = i;
this._nameOrdinalMeta = dimensionInfo.ordinalMeta;
}
if (otherDims.itemId === 0) {
this._idDimIdx = i;
this._idOrdinalMeta = dimensionInfo.ordinalMeta;
}
}
this.dimensions = dimensionNames;
......@@ -417,39 +435,45 @@ class List<
/**
* Initialize from data
* @param data source or data or data provider.
* @param nameLIst The name of a datum is used on data diff and
* @param nameList The name of a datum is used on data diff and
* default label/tooltip.
* A name can be specified in encode.itemName,
* or dataItem.name (only for series option data),
* or provided in nameList from outside.
*/
initData(
data: any,
data: Source | OptionSourceData | DataProvider,
nameList?: string[],
dimValueGetter?: DimValueGetter
): void {
const notProvider = isSourceInstance(data) || zrUtil.isArrayLike(data);
if (notProvider) {
data = new DefaultDataProvider(data, this.dimensions.length);
}
const provider: DataProvider = notProvider
? new DefaultDataProvider(data as Source | OptionSourceData, this.dimensions.length)
: data as DataProvider;
if (__DEV__) {
if (!notProvider
&& (typeof data.getItem !== 'function' || typeof data.count !== 'function')
) {
throw new Error('Inavlid data provider.');
}
zrUtil.assert(
notProvider || (
zrUtil.isFunction(provider.getItem)
&& zrUtil.isFunction(provider.count)
),
'Inavlid data provider.'
);
}
this._rawData = data;
this._rawData = provider;
const sourceFormat = provider.getSource().sourceFormat;
// Clear
this._storage = {};
this._indices = null;
this._dontMakeIdFromName =
this._idDimIdx != null
|| sourceFormat === SOURCE_FORMAT_TYPED_ARRAY // Cosndier performance.
|| !!provider.fillStorage;
this._nameList = nameList || [];
this._nameList = (nameList || []).slice();
this._idList = [];
this._nameRepeatCount = {};
......@@ -458,9 +482,7 @@ class List<
this.hasItemOption = false;
}
this.defaultDimValueGetter = defaultDimValueGetters[
this._rawData.getSource().sourceFormat
];
this.defaultDimValueGetter = defaultDimValueGetters[sourceFormat];
// Default dim value getter
this._dimValueGetter = dimValueGetter = dimValueGetter
|| this.defaultDimValueGetter;
......@@ -469,10 +491,10 @@ class List<
// Reset raw extent.
this._rawExtent = {};
this._initDataFromProvider(0, data.count());
this._initDataFromProvider(0, provider.count());
// If data has no item option.
if (data.pure) {
if (provider.pure) {
this.hasItemOption = false;
}
}
......@@ -557,6 +579,9 @@ class List<
if (names) {
this._nameList[idx] = names[sourceIdx];
if (!this._dontMakeIdFromName) {
makeIdFromName(this, idx);
}
}
}
......@@ -581,24 +606,15 @@ class List<
const nameList = this._nameList;
const idList = this._idList;
const rawExtent = this._rawExtent;
this._nameRepeatCount = {};
let nameDimIdx;
const sourceFormat = rawData.getSource().sourceFormat;
const isFormatOriginal = sourceFormat === SOURCE_FORMAT_ORIGINAL;
for (let i = 0; i < dimLen; i++) {
const dim = dimensions[i];
if (!rawExtent[dim]) {
rawExtent[dim] = getInitialExtent();
}
const dimInfo = dimensionInfoMap[dim];
if (dimInfo.otherDims.itemName === 0) {
nameDimIdx = this._nameDimIdx = i;
}
if (dimInfo.otherDims.itemId === 0) {
this._idDimIdx = i;
}
prepareStorage(storage, dimInfo, end, append);
prepareStorage(storage, dimensionInfoMap[dim], end, append);
}
const storageArr = this._storageArr = map(dimensions, (dim) => {
......@@ -637,46 +653,25 @@ class List<
val > dimRawExtent[1] && (dimRawExtent[1] = val);
}
// ??? FIXME not check by pure but sourceFormat?
// TODO refactor these logic.
if (!rawData.pure) {
let name: string = nameList[idx];
if (dataItem && name == null) {
// If dataItem is {name: ...}, it has highest priority.
// That is appropriate for many common cases.
if ((dataItem as any).name != null) {
// There is no other place to persistent dataItem.name,
// so save it to nameList.
nameList[idx] = name = convertOptionIdName((dataItem as any).name, null);
}
else if (nameDimIdx != null) {
const nameDim = dimensions[nameDimIdx];
const nameDimChunk = storage[nameDim];
if (nameDimChunk) {
const ordinalMeta = dimensionInfoMap[nameDim].ordinalMeta;
name = convertOptionIdName(
(ordinalMeta && ordinalMeta.categories.length)
? ordinalMeta.categories[nameDimChunk[idx] as number]
: nameDimChunk[idx],
null
);
}
}
// If dataItem is {name: ...} or {id: ...}, it has highest priority.
// This kind of ids and names are always stored `_nameList` and `_idList`.
if (isFormatOriginal && !rawData.pure && dataItem) {
const itemName = (dataItem as any).name;
if (nameList[idx] == null && itemName != null) {
nameList[idx] = convertOptionIdName(itemName, null);
}
// Try using the id in option
// id or name is used on dynamical data, mapping old and new items.
let id: string = dataItem == null ? null : convertOptionIdName((dataItem as any).id, null);
if (id == null && name != null) {
// Use name as id and add counter to avoid same name
id = getIdFromName(this, name);
const itemId = (dataItem as any).id;
if (idList[idx] == null && itemId != null) {
idList[idx] = convertOptionIdName(itemId, null);
}
id != null && (idList[idx] = id);
}
if (!this._dontMakeIdFromName) {
makeIdFromName(this, idx);
}
}
}
if (!rawData.persistent && rawData.clean) {
// Clean unused data if data source is typed array.
rawData.clean();
......@@ -1132,9 +1127,14 @@ class List<
*/
getName(idx: number): string {
const rawIndex = this.getRawIndex(idx);
return this._nameList[rawIndex]
|| convertOptionIdName(getRawValueFromStore(this, this._nameDimIdx, rawIndex), '')
|| '';
let name = this._nameList[rawIndex];
if (name == null && this._nameDimIdx != null) {
name = getIdNameFromStore(this, this._nameDimIdx, this._nameOrdinalMeta, rawIndex);
}
if (name == null) {
name = '';
}
return name;
}
/**
......@@ -2062,22 +2062,18 @@ class List<
});
};
getRawValueFromStore = function (
list: List, dimIndex: number, rawIndex: number
): ParsedValue | OrdinalRawValue {
getIdNameFromStore = function (
list: List, dimIdx: number, ordinalMeta: OrdinalMeta, rawIndex: number
): string {
let val;
if (dimIndex != null) {
const dim = list.dimensions[dimIndex];
const chunk = list._storage[dim];
if (chunk) {
val = chunk[rawIndex];
const ordinalMeta = list._dimensionInfos[dim].ordinalMeta;
if (ordinalMeta && ordinalMeta.categories.length) {
val = ordinalMeta.categories[val as OrdinalNumber];
}
const chunk = list._storageArr[dimIdx];
if (chunk) {
val = chunk[rawIndex];
if (ordinalMeta && ordinalMeta.categories.length) {
val = ordinalMeta.categories[val as OrdinalNumber];
}
}
return val;
return convertOptionIdName(val, null);
};
getIndicesCtor = function (list: List): DataArrayLikeConstructor {
......@@ -2123,37 +2119,16 @@ class List<
return -1;
};
getIdFromName = function (list: List, name: string) {
const nameRepeatCount = list._nameRepeatCount;
nameRepeatCount[name] = nameRepeatCount[name] || 0;
let id = name;
if (nameRepeatCount[name] > 0) {
id += '__ec__' + nameRepeatCount[name];
}
nameRepeatCount[name]++;
return id;
};
/**
* @see the comment of `List['getId']`.
*/
getId = function (list: List, rawIndex: number): string {
let id = list._idList[rawIndex];
if (id == null) {
id = convertOptionIdName(getRawValueFromStore(list, list._idDimIdx, rawIndex), null);
if (id == null && list._idDimIdx != null) {
id = getIdNameFromStore(list, list._idDimIdx, list._idOrdinalMeta, rawIndex);
}
if (id == null) {
// Use name
id = convertOptionIdName(getRawValueFromStore(list, list._nameDimIdx, rawIndex), null);
if (id != null) {
id = getIdFromName(list, id);
// Cache the id. Avoid getting twice.
list._idList[rawIndex] = id;
}
else {
// FIXME Check the usage in graph, should not use prefix.
id = ID_PREFIX + rawIndex;
}
id = ID_PREFIX + rawIndex;
}
return id;
};
......@@ -2253,6 +2228,32 @@ class List<
target._calculationInfo = zrUtil.extend({}, source._calculationInfo);
};
makeIdFromName = function (list: List, idx: number): void {
const nameList = list._nameList;
const idList = list._idList;
const nameDimIdx = list._nameDimIdx;
const idDimIdx = list._idDimIdx;
let name = nameList[idx];
let id = idList[idx];
if (name == null && nameDimIdx != null) {
nameList[idx] = name = getIdNameFromStore(list, nameDimIdx, list._nameOrdinalMeta, idx);
}
if (id == null && idDimIdx != null) {
idList[idx] = id = getIdNameFromStore(list, idDimIdx, list._idOrdinalMeta, idx);
}
if (id == null && name != null) {
const nameRepeatCount = list._nameRepeatCount;
const nmCnt = nameRepeatCount[name] = (nameRepeatCount[name] || 0) + 1;
id = name;
if (nmCnt > 1) {
id += '__ec__' + nmCnt;
}
idList[idx] = id;
}
};
})();
}
......
......@@ -39,10 +39,17 @@ import {
import List from '../List';
export interface DataProvider {
// If data is pure without style configuration
pure: boolean;
// If data is persistent and will not be released after use.
persistent: boolean;
/**
* true: all of the value are in primitive type (in type `OptionDataValue`).
* false: Not sure whether any of them is non primitive type (in type `OptionDataItemObject`).
* Like `data: [ { value: xx, itemStyle: {...} }, ...]`
* At present it only happen in `SOURCE_FORMAT_ORIGINAL`.
*/
pure?: boolean;
/**
* If data is persistent and will not be released after use.
*/
persistent?: boolean;
getSource(): Source;
count(): number;
......@@ -53,8 +60,8 @@ export interface DataProvider {
out: ArrayLike<ParsedValue>[],
extent: number[][]
): void
appendData(newData: ArrayLike<OptionDataItem>): void;
clean(): void;
appendData?(newData: ArrayLike<OptionDataItem>): void;
clean?(): void;
}
......
......@@ -539,6 +539,9 @@ function makeComparableKey(val: unknown): string {
}
export function convertOptionIdName(idOrName: unknown, defaultValue: string): string {
if (idOrName == null) {
return defaultValue;
}
const type = typeof idOrName;
return type === 'string'
? idOrName as string
......
......@@ -18,10 +18,18 @@
* under the License.
*/
/* global Float32Array */
import List from '../../../../src/data/List';
import Model from '../../../../src/model/Model';
import { createSourceFromSeriesDataOption } from '../../../../src/data/Source';
import { createSourceFromSeriesDataOption, Source, createSource } from '../../../../src/data/Source';
import { OptionDataItemObject, OptionDataValue, SOURCE_FORMAT_ARRAY_ROWS } from '../../../../src/util/types';
import DataDimensionInfo from '../../../../src/data/DataDimensionInfo';
import OrdinalMeta from '../../../../src/data/OrdinalMeta';
const ID_PREFIX = 'e\0\0';
const NAME_REPEAT_PREFIX = '__ec__';
describe('List', function () {
......@@ -53,10 +61,13 @@ describe('List', function () {
it('Data with option 1d', function () {
const list = new List(['x', 'y'], new Model());
list.initData([1, {
value: 2,
somProp: 'foo'
}]);
list.initData([
1,
{
value: 2,
somProp: 'foo'
} as OptionDataItemObject<OptionDataValue>
]);
expect(list.getItemModel(1).get('somProp' as any)).toEqual('foo');
expect(list.getItemModel(0).get('somProp' as any)).toBeNull();
});
......@@ -162,13 +173,13 @@ describe('List', function () {
const typedArray = new Float32Array([10, 10, 20, 20]);
const source = createSourceFromSeriesDataOption(typedArray);
list.initData({
count: function () {
count: function (): number {
return typedArray.length / 2;
},
getItem: function (idx: number) {
getItem: function (idx: number): number[] {
return [typedArray[idx * 2], typedArray[idx * 2 + 1]];
},
getSource: function () {
getSource: function (): Source {
return source;
}
});
......@@ -199,4 +210,348 @@ describe('List', function () {
expect(list.indicesOfNearest('value', 50.5, 0.5)).toEqual([7]);
});
});
describe('id_and_name', function () {
function makeOneByOneChecker(list: List) {
let getIdDataIndex = 0;
let getNameDataIndex = 0;
return {
idEqualsTo: function (expectedId: string): void {
expect(list.getId(getIdDataIndex)).toEqual(expectedId);
getIdDataIndex++;
},
nameEqualsTo: function (expectedName: string): void {
expect(list.getName(getNameDataIndex)).toEqual(expectedName);
getNameDataIndex++;
},
currGetIdDataIndex: function (): number {
return getIdDataIndex;
},
currGetNameDataIndex: function (): number {
return getNameDataIndex;
}
};
}
describe('only_name_declared', function () {
function doChecks(list: List) {
const oneByOne = makeOneByOneChecker(list);
oneByOne.idEqualsTo('a');
oneByOne.idEqualsTo('b');
oneByOne.idEqualsTo(`b${NAME_REPEAT_PREFIX}2`);
oneByOne.idEqualsTo('c');
oneByOne.idEqualsTo(`${ID_PREFIX}4`);
oneByOne.idEqualsTo(`c${NAME_REPEAT_PREFIX}2`);
oneByOne.idEqualsTo('d');
oneByOne.idEqualsTo(`c${NAME_REPEAT_PREFIX}3`);
oneByOne.nameEqualsTo('a');
oneByOne.nameEqualsTo('b');
oneByOne.nameEqualsTo('b');
oneByOne.nameEqualsTo('c');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('c');
oneByOne.nameEqualsTo('d');
oneByOne.nameEqualsTo('c');
}
it('sourceFormatOriginal', function () {
const list = new List(['x', 'y'], new Model());
list.initData([
{ value: 10, name: 'a' },
{ value: 20, name: 'b' },
{ value: 30, name: 'b' },
{ value: 40, name: 'c' },
{ value: 50 }, // name not declared
{ value: 60, name: 'c' },
{ value: 70, name: 'd' },
{ value: 80, name: 'c' }
]);
doChecks(list);
});
it('sourceFormatArrayRows', function () {
const list = new List(
[
'x',
{ name: 'q', type: 'ordinal', otherDims: { itemName: 0 } }
],
new Model()
);
const source = createSource(
[
[ 10, 'a' ],
[ 20, 'b' ],
[ 30, 'b' ],
[ 40, 'c' ],
[ 50, null ],
[ 60, 'c' ],
[ 70, 'd' ],
[ 80, 'c' ]
],
{
seriesLayoutBy: 'column',
sourceHeader: 0,
dimensions: null
},
SOURCE_FORMAT_ARRAY_ROWS,
{
itemName: 1
}
);
list.initData(source);
doChecks(list);
});
});
describe('id_name_declared_sourceFormat_original', function () {
it('sourceFormatOriginal', function () {
const list = new List(['x'], new Model());
const oneByOne = makeOneByOneChecker(list);
list.initData([
{ value: 0, id: 'myId_10' },
{ value: 10, id: 555 }, // numeric id.
{ value: 20, id: '666%' },
{ value: 30, id: 'myId_good', name: 'b' },
{ value: 40, name: 'b' },
{ value: 50, id: null },
{ value: 60, id: undefined },
{ value: 70, id: NaN },
{ value: 80, id: '' },
{ value: 90, name: 'b' },
{ value: 100 },
{ value: 110, id: 'myId_better' },
{ value: 120, id: 'myId_better' } // duplicated id.
]);
oneByOne.idEqualsTo('myId_10');
oneByOne.idEqualsTo('555');
oneByOne.idEqualsTo('666%');
oneByOne.idEqualsTo('myId_good');
oneByOne.idEqualsTo('b');
oneByOne.idEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`);
oneByOne.idEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`);
oneByOne.idEqualsTo('NaN');
oneByOne.idEqualsTo('');
oneByOne.idEqualsTo(`b${NAME_REPEAT_PREFIX}2`);
oneByOne.idEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`);
oneByOne.idEqualsTo('myId_better');
oneByOne.idEqualsTo('myId_better');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('b');
oneByOne.nameEqualsTo('b');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('b');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
list.appendData([
{ value: 200, id: 'myId_best' },
{ value: 210, id: 999 }, // numeric id.
{ value: 220, id: '777px' },
{ value: 230, name: 'b' },
{ value: 240 }
]);
oneByOne.idEqualsTo('myId_best');
oneByOne.idEqualsTo('999');
oneByOne.idEqualsTo('777px');
oneByOne.idEqualsTo(`b${NAME_REPEAT_PREFIX}3`);
oneByOne.idEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`);
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('b');
oneByOne.nameEqualsTo('');
list.appendValues(
[
[300],
[310],
[320]
],
[
'b',
'c',
null
]
);
oneByOne.idEqualsTo(`b${NAME_REPEAT_PREFIX}4`);
oneByOne.idEqualsTo('c');
oneByOne.idEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`);
oneByOne.nameEqualsTo('b');
oneByOne.nameEqualsTo('c');
oneByOne.nameEqualsTo('');
});
});
describe('id_name_declared_sourceFormat_arrayRows', function () {
function makeChecker(list: List) {
const oneByOne = makeOneByOneChecker(list);
return {
checkAfterInitData() {
oneByOne.idEqualsTo('myId_10');
oneByOne.idEqualsTo('555');
oneByOne.idEqualsTo('666%');
oneByOne.idEqualsTo('myId_good');
oneByOne.idEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`);
oneByOne.idEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`);
oneByOne.idEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`);
oneByOne.idEqualsTo('NaN');
oneByOne.idEqualsTo('');
oneByOne.idEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`);
oneByOne.idEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`);
oneByOne.idEqualsTo('myId_better');
oneByOne.idEqualsTo('myId_better');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('b');
oneByOne.nameEqualsTo('b');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('b');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
},
checkAfterAppendData() {
oneByOne.idEqualsTo('myId_best');
oneByOne.idEqualsTo('999');
oneByOne.idEqualsTo('777px');
oneByOne.idEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`);
oneByOne.idEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`);
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('');
oneByOne.nameEqualsTo('b');
oneByOne.nameEqualsTo('');
},
checkAfterAppendValues() {
oneByOne.idEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`);
oneByOne.idEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`);
oneByOne.idEqualsTo(`${ID_PREFIX}${oneByOne.currGetIdDataIndex()}`);
oneByOne.nameEqualsTo('b');
oneByOne.nameEqualsTo('c');
oneByOne.nameEqualsTo('');
}
};
}
it('no_ordinalMeta', function () {
testArrayRowsInSource([
{ name: 'x', type: 'number' },
{ name: 'p', type: 'ordinal', otherDims: { itemId: 0 } },
{ name: 'q', type: 'ordinal', otherDims: { itemName: 0 } }
]);
});
it('has_ordinalMeta', function () {
const ordinalMetaP = new OrdinalMeta({
categories: [],
needCollect: true,
deduplication: true
});
const ordinalMetaQ = new OrdinalMeta({
categories: [],
needCollect: true,
deduplication: true
});
testArrayRowsInSource([
{ name: 'x', type: 'number' },
{ name: 'p', type: 'ordinal', otherDims: { itemId: 0 }, ordinalMeta: ordinalMetaP },
{ name: 'q', type: 'ordinal', otherDims: { itemName: 0 }, ordinalMeta: ordinalMetaQ }
]);
});
function testArrayRowsInSource(dimensionsInfo: DataDimensionInfo[]): void {
const list = new List(dimensionsInfo, new Model());
const checker = makeChecker(list);
const source = createSource(
[
[0, 'myId_10', null],
[10, 555, null], // numeric id.
[20, '666%', null],
[30, 'myId_good', 'b'],
[40, null, 'b'],
[50, null, null],
[60, undefined, null],
[70, NaN, null],
[80, '', null],
[90, null, 'b'],
[100, null, null],
[110, 'myId_better', null],
[120, 'myId_better', null] // duplicated id.
],
{
seriesLayoutBy: 'column',
sourceHeader: 0,
dimensions: null
},
SOURCE_FORMAT_ARRAY_ROWS,
{
itemId: 1,
itemName: 2
}
);
list.initData(source);
checker.checkAfterInitData();
list.appendData([
[ 200, 'myId_best', null ],
[ 210, 999, null ], // numeric id.
[ 220, '777px', null],
[ 230, null, 'b' ],
[ 240, null, null ]
]);
checker.checkAfterAppendData();
list.appendValues(
[
[300],
[310],
[320]
],
[
'b',
'c',
null
]
);
checker.checkAfterAppendValues();
}
});
});
});
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册