List.ts 75.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/*
* 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.
*/

S
sushuang 已提交
20 21
/* global Float64Array, Int32Array, Uint32Array, Uint16Array */

L
lang 已提交
22 23 24
/**
 * List for data storage
 */
L
lang 已提交
25

S
sushuang 已提交
26
import * as zrUtil from 'zrender/src/core/util';
S
sushuang 已提交
27 28
import Model from '../model/Model';
import DataDiffer from './DataDiffer';
P
pissang 已提交
29
import Source from './Source';
30
import {DefaultDataProvider, DataProvider} from './helper/dataProvider';
31
import {summarizeDimensions, DimensionSummary} from './helper/dimensionHelper';
S
fix:  
SHUANG SU 已提交
32
import DataDimensionInfo from './DataDimensionInfo';
33 34 35
import {ArrayLike, Dictionary, FunctionPropertyNames} from 'zrender/src/core/types';
import Element from 'zrender/src/Element';
import {
36
    DimensionIndex, DimensionName, DimensionLoose, OptionDataItem,
37
    ParsedValue, ParsedValueNumeric, OrdinalNumber, DimensionUserOuput, ModelOption
38 39 40
} from '../util/types';
import {parseDate} from '../util/number';
import {isDataItemOption} from '../util/model';
41
import { getECData } from "../util/ecData";
42
import { PathStyleProps } from 'zrender/src/graphic/Path';
P
pissang 已提交
43 44
import type Graph from './Graph';
import type Tree from './Tree';
45
import type { VisualMeta } from '../component/visualMap/VisualMapModel';
46

S
sushuang 已提交
47

48
const isObject = zrUtil.isObject;
S
sushuang 已提交
49

50 51
const UNDEFINED = 'undefined';
const INDEX_NOT_FOUND = -1;
S
sushuang 已提交
52

53 54
// Use prefix to avoid index to be the same as otherIdList[idx],
// which will cause weird udpate animation.
55
const ID_PREFIX = 'e\0\0';
56

57
const dataCtors = {
S
sushuang 已提交
58 59 60 61
    'float': typeof Float64Array === UNDEFINED
        ? Array : Float64Array,
    'int': typeof Int32Array === UNDEFINED
        ? Array : Int32Array,
S
sushuang 已提交
62 63 64 65 66 67
    // Ordinal data type can be string or int
    'ordinal': Array,
    'number': Array,
    'time': Array
};

68 69
export type ListDimensionType = keyof typeof dataCtors;

S
sushuang 已提交
70 71
// Caution: MUST not use `new CtorUint32Array(arr, 0, len)`, because the Ctor of array is
// different from the Ctor of typed array.
72 73 74
const CtorUint32Array = typeof Uint32Array === UNDEFINED ? Array : Uint32Array;
const CtorInt32Array = typeof Int32Array === UNDEFINED ? Array : Int32Array;
const CtorUint16Array = typeof Uint16Array === UNDEFINED ? Array : Uint16Array;
P
pissang 已提交
75

76 77 78 79 80 81 82 83 84 85 86
type DataTypedArray = Uint32Array | Int32Array | Uint16Array | Float64Array;
type DataTypedArrayConstructor = typeof Uint32Array | typeof Int32Array | typeof Uint16Array | typeof Float64Array;
type DataArrayLikeConstructor = typeof Array | DataTypedArrayConstructor;


type DimValueGetter = (
    this: List,
    dataItem: any,
    dimName: DimensionName,
    dataIndex: number,
    dimIndex: DimensionIndex
87
) => ParsedValue;
88

89
type DataValueChunk = ArrayLike<ParsedValue>;
90 91 92 93 94 95 96 97
type DataStorage = {[dimName: string]: DataValueChunk[]};
type NameRepeatCount = {[name: string]: number};


type ItrParamDims = DimensionLoose | Array<DimensionLoose>;
// If Ctx not specified, use List as Ctx
type CtxOrList<Ctx> = unknown extends Ctx ? List : Ctx;
type EachCb0<Ctx> = (this: CtxOrList<Ctx>, idx: number) => void;
98 99
type EachCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, idx: number) => void;
type EachCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, y: ParsedValue, idx: number) => void;
100 101
type EachCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => void;
type FilterCb0<Ctx> = (this: CtxOrList<Ctx>, idx: number) => boolean;
102 103
type FilterCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, idx: number) => boolean;
type FilterCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, y: ParsedValue, idx: number) => boolean;
104 105
type FilterCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => boolean;
type MapArrayCb0<Ctx> = (this: CtxOrList<Ctx>, idx: number) => any;
106 107
type MapArrayCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, idx: number) => any;
type MapArrayCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, y: ParsedValue, idx: number) => any;
108
type MapArrayCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => any;
109 110 111 112
type MapCb1<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, idx: number) => ParsedValue | ParsedValue[];
type MapCb2<Ctx> = (this: CtxOrList<Ctx>, x: ParsedValue, y: ParsedValue, idx: number) =>
    ParsedValue | ParsedValue[];
type MapCb<Ctx> = (this: CtxOrList<Ctx>, ...args: any) => ParsedValue | ParsedValue[];
113

S
sushuang 已提交
114

115
const TRANSFERABLE_PROPERTIES = [
116 117 118 119
    'hasItemOption', '_nameList', '_idList', '_invertedIndicesMap',
    '_rawData', '_chunkSize', '_chunkCount', '_dimValueGetter',
    '_count', '_rawCount', '_nameDimIdx', '_idDimIdx'
];
120
const CLONE_PROPERTIES = [
121
    '_extent', '_approximateExtent', '_rawExtent'
S
sushuang 已提交
122 123
];

124 125
export interface DefaultDataVisual {
    style: PathStyleProps
126 127
    // Draw type determined which prop should be set with encoded color.
    // It's only available on the global visual. Use getVisual('drawType') to access it.
128
    // It will be set in visual/style.ts module in the first priority.
129
    drawType: 'fill' | 'stroke'
130 131 132

    symbol?: string
    symbolSize?: number | number[]
133
    symbolRotate?: number
134 135 136 137 138 139 140 141
    symbolKeepAspect?: boolean

    liftZ?: number
    // For legend.
    legendSymbol?: string

    // visualMap will inject visualMeta data
    visualMeta?: VisualMeta[]
142 143 144

    // If color is encoded from palette
    colorFromPalette?: boolean
145
}
L
lang 已提交
146

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
// -----------------------------
// Internal method declarations:
// -----------------------------
let defaultDimValueGetters: {[sourceFormat: string]: DimValueGetter};
let prepareInvertedIndex: (list: List) => void;
let getRawValueFromStore: (list: List, dimIndex: number, rawIndex: number) => any;
let getIndicesCtor: (list: List) => DataArrayLikeConstructor;
let prepareChunks: (
    storage: DataStorage, dimInfo: DataDimensionInfo, chunkSize: number, chunkCount: number, end: number
) => void;
let getRawIndexWithoutIndices: (this: List, idx: number) => number;
let getRawIndexWithIndices: (this: List, idx: number) => number;
let getId: (list: List, rawIndex: number) => string;
let normalizeDimensions: (dimensions: ItrParamDims) => Array<DimensionLoose>;
let validateDimensions: (list: List, dims: DimensionName[]) => void;
let cloneListForMapAndSample: (original: List, excludeDimensions: DimensionName[]) => List;
let cloneDimStore: (originalDimStore: DataValueChunk[]) => DataValueChunk[];
let getInitialExtent: () => [number, number];
let setItemDataAndSeriesIndex: (this: Element, child: Element) => void;
let transferProperties: (target: List, source: List) => void;

168 169 170 171
class List<
    HostModel extends Model = Model,
    Visual extends DefaultDataVisual = DefaultDataVisual
> {
L
lang 已提交
172

173
    readonly type = 'list';
L
lang 已提交
174

175
    readonly dimensions: string[];
176

177 178
    // Infomation of each data dimension, like data type.
    private _dimensionInfos: {[dimName: string]: DataDimensionInfo};
179

P
pissang 已提交
180
    readonly hostModel: HostModel;
181

182
    readonly dataType: string;
183

P
pissang 已提交
184 185 186
    /**
     * Host graph if List is used to store graph nodes / edges.
     */
1
100pah 已提交
187
    readonly graph?: Graph;
P
pissang 已提交
188 189 190
    /**
     * Host tree if List is used to store tree ndoes.
     */
1
100pah 已提交
191
    readonly tree?: Tree;
P
pissang 已提交
192

193 194 195
    // Indices stores the indices of data subset after filtered.
    // This data subset will be used in chart.
    private _indices: ArrayLike<any>;
S
sushuang 已提交
196

197 198 199 200 201
    private _count: number = 0;
    private _rawCount: number = 0;
    private _storage: DataStorage = {};
    private _nameList: string[] = [];
    private _idList: string[] = [];
S
sushuang 已提交
202

203 204 205
    // Models of data option is stored sparse for optimizing memory cost
    // Never used yet (not used yet).
    // private _optionModels: Model[] = [];
S
sushuang 已提交
206

207 208
    // Global visual properties after visual coding
    private _visual: Dictionary<any> = {};
S
sushuang 已提交
209

210 211
    // Globel layout properties.
    private _layout: Dictionary<any> = {};
S
sushuang 已提交
212

213 214
    // Item visual properties after visual coding
    private _itemVisuals: Dictionary<any>[] = [];
S
sushuang 已提交
215

216 217
    // Item layout properties after layout
    private _itemLayouts: any[] = [];
S
sushuang 已提交
218

219 220
    // Graphic elemnents
    private _graphicEls: Element[] = [];
O
Ovilia 已提交
221

222 223
    // Max size of each chunk.
    private _chunkSize: number = 1e5;
O
Ovilia 已提交
224

225
    private _chunkCount: number = 0;
L
lang 已提交
226

227
    private _rawData: DataProvider;
P
pah100 已提交
228

229 230 231
    // Raw extent will not be cloned, but only transfered.
    // It will not be calculated util needed.
    private _rawExtent: {[dimName: string]: [number, number]} = {};
L
lang 已提交
232

233
    private _extent: {[dimName: string]: [number, number]} = {};
L
lang 已提交
234

235 236
    // key: dim, value: extent
    private _approximateExtent: {[dimName: string]: [number, number]} = {};
S
sushuang 已提交
237

238
    private _dimensionsSummary: DimensionSummary;
P
pah100 已提交
239

240
    private _invertedIndicesMap: {[dimName: string]: ArrayLike<number>};
L
lang 已提交
241

242
    private _calculationInfo: {[key: string]: any} = {};
P
pah100 已提交
243

244 245 246 247 248 249 250 251
    // User output info of this data.
    // DO NOT use it in other places!
    // When preparing user params for user callbacks, we have
    // to clone these inner data structures to prevent users
    // from modifying them to effect built-in logic. And for
    // performance consideration we make this `userOutput` to
    // avoid clone them too many times.
    readonly userOutput: DimensionUserOuput;
O
Ovilia 已提交
252

253 254
    // If each data item has it's own option
    hasItemOption: boolean = true;
S
sushuang 已提交
255

256 257 258 259
    // @readonly
    defaultDimValueGetter: DimValueGetter;
    private _dimValueGetter: DimValueGetter;
    private _dimValueGetterArrayRows: DimValueGetter;
L
lang 已提交
260

261 262 263
    private _nameRepeatCount: NameRepeatCount;
    private _nameDimIdx: number;
    private _idDimIdx: number;
S
sushuang 已提交
264

265
    private __wrappedMethods: string[];
S
sushuang 已提交
266

267 268 269 270 271
    // Methods that create a new list based on this list should be listed here.
    // Notice that those method should `RETURN` the new list.
    TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map'];
    // Methods that change indices of this list should be listed here.
    CHANGABLE_METHODS = ['filterSelf', 'selectRange'];
S
sushuang 已提交
272

273 274

    /**
275 276 277
     * @param dimensions
     *        For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].
     *        Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
278
     */
P
pissang 已提交
279
    constructor(dimensions: Array<string | object | DataDimensionInfo>, hostModel: HostModel) {
280 281
        dimensions = dimensions || ['x', 'y'];

282 283 284
        const dimensionInfos: Dictionary<DataDimensionInfo> = {};
        const dimensionNames = [];
        const invertedIndicesMap: Dictionary<number[]> = {};
285

286
        for (let i = 0; i < dimensions.length; i++) {
287
            // Use the original dimensions[i], where other flag props may exists.
288
            const dimInfoInput = dimensions[i];
289

290
            const dimensionInfo: DataDimensionInfo =
291 292 293 294 295 296
                zrUtil.isString(dimInfoInput)
                ? new DataDimensionInfo({name: dimInfoInput})
                : !(dimInfoInput instanceof DataDimensionInfo)
                ? new DataDimensionInfo(dimInfoInput)
                : dimInfoInput;

297
            const dimensionName = dimensionInfo.name;
298 299 300 301 302
            dimensionInfo.type = dimensionInfo.type || 'float';
            if (!dimensionInfo.coordDim) {
                dimensionInfo.coordDim = dimensionName;
                dimensionInfo.coordDimIndex = 0;
            }
303

304 305 306
            dimensionInfo.otherDims = dimensionInfo.otherDims || {};
            dimensionNames.push(dimensionName);
            dimensionInfos[dimensionName] = dimensionInfo;
L
lang 已提交
307

308
            dimensionInfo.index = i;
S
sushuang 已提交
309

310 311 312 313
            if (dimensionInfo.createInvertedIndices) {
                invertedIndicesMap[dimensionName] = [];
            }
        }
S
sushuang 已提交
314

315 316 317 318 319 320 321 322 323 324 325
        this.dimensions = dimensionNames;
        this._dimensionInfos = dimensionInfos;
        this.hostModel = hostModel;

        // Cache summary info for fast visit. See "dimensionHelper".
        this._dimensionsSummary = summarizeDimensions(this);

        this._invertedIndicesMap = invertedIndicesMap;

        this.userOutput = this._dimensionsSummary.userOutput;
    }
326 327

    /**
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
     * The meanings of the input parameter `dim`:
     *
     * + If dim is a number (e.g., `1`), it means the index of the dimension.
     *   For example, `getDimension(0)` will return 'x' or 'lng' or 'radius'.
     * + If dim is a number-like string (e.g., `"1"`):
     *     + If there is the same concrete dim name defined in `this.dimensions`, it means that concrete name.
     *     + If not, it will be converted to a number, which means the index of the dimension.
     *        (why? because of the backward compatbility. We have been tolerating number-like string in
     *        dimension setting, although now it seems that it is not a good idea.)
     *     For example, `visualMap[i].dimension: "1"` is the same meaning as `visualMap[i].dimension: 1`,
     *     if no dimension name is defined as `"1"`.
     * + If dim is a not-number-like string, it means the concrete dim name.
     *   For example, it can be be default name `"x"`, `"y"`, `"z"`, `"lng"`, `"lat"`, `"angle"`, `"radius"`,
     *   or customized in `dimensions` property of option like `"age"`.
     *
     * Get dimension name
     * @param dim See above.
     * @return Concrete dim name.
346
     */
347 348 349 350 351 352 353 354 355
    getDimension(dim: DimensionLoose): DimensionName {
        if (typeof dim === 'number'
            // If being a number-like string but not being defined a dimension name.
            || (!isNaN(dim as any) && !this._dimensionInfos.hasOwnProperty(dim))
        ) {
            dim = this.dimensions[dim as DimensionIndex];
        }
        return dim as DimensionName;
    }
S
sushuang 已提交
356 357

    /**
358 359 360 361
     * Get type and calculation info of particular dimension
     * @param dim
     *        Dimension can be concrete names like x, y, z, lng, lat, angle, radius
     *        Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
S
sushuang 已提交
362
     */
363 364 365 366
    getDimensionInfo(dim: DimensionLoose): DataDimensionInfo {
        // Do not clone, because there may be categories in dimInfo.
        return this._dimensionInfos[this.getDimension(dim)];
    }
S
sushuang 已提交
367 368

    /**
369
     * concrete dimension name list on coord.
S
sushuang 已提交
370
     */
371 372 373
    getDimensionsOnCoord(): DimensionName[] {
        return this._dimensionsSummary.dataDimsOnCoord.slice();
    }
374 375

    /**
376 377
     * @param coordDim
     * @param idx A coordDim may map to more than one data dim.
378 379
     *        If not specified, return the first dim not extra.
     * @return concrete data dim. If not found, return null/undefined
380
     */
381 382
    mapDimension(coordDim: DimensionName): DimensionName;
    mapDimension(coordDim: DimensionName, idx: number): DimensionName;
383
    mapDimension(coordDim: DimensionName, idx?: number): DimensionName {
384
        const dimensionsSummary = this._dimensionsSummary;
385 386 387 388

        if (idx == null) {
            return dimensionsSummary.encodeFirstDimNotExtra[coordDim] as any;
        }
L
lang 已提交
389

390
        const dims = dimensionsSummary.encode[coordDim];
391 392 393 394 395 396 397
        return dims ? dims[idx as number] as any : null;
    }

    mapDimensionsAll(coordDim: DimensionName): DimensionName[] {
        const dimensionsSummary = this._dimensionsSummary;
        const dims = dimensionsSummary.encode[coordDim];
        return (dims || []).slice();
S
sushuang 已提交
398
    }
L
lang 已提交
399

400 401 402 403 404 405 406 407 408 409 410 411 412 413 414
    /**
     * Initialize from data
     * @param data source or data or data provider.
     * @param nameLIst The name of a datum is used on data diff and
     *        defualt 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,
        nameList?: string[],
        dimValueGetter?: DimValueGetter
    ): void {

415
        const notProvider = data instanceof Source || zrUtil.isArrayLike(data);
416 417 418
        if (notProvider) {
            data = new DefaultDataProvider(data, this.dimensions.length);
        }
L
lang 已提交
419

420 421 422 423 424 425 426
        if (__DEV__) {
            if (!notProvider
                && (typeof data.getItem !== 'function' || typeof data.count !== 'function')
            ) {
                throw new Error('Inavlid data provider.');
            }
        }
S
sushuang 已提交
427

428
        this._rawData = data;
S
sushuang 已提交
429

430 431 432
        // Clear
        this._storage = {};
        this._indices = null;
S
sushuang 已提交
433

434
        this._nameList = nameList || [];
435

436
        this._idList = [];
437

438
        this._nameRepeatCount = {};
439

440 441
        if (!dimValueGetter) {
            this.hasItemOption = false;
L
lang 已提交
442
        }
443

444 445 446 447 448 449 450
        this.defaultDimValueGetter = defaultDimValueGetters[
            this._rawData.getSource().sourceFormat
        ];
        // Default dim value getter
        this._dimValueGetter = dimValueGetter = dimValueGetter
            || this.defaultDimValueGetter;
        this._dimValueGetterArrayRows = defaultDimValueGetters.arrayRows;
L
lang 已提交
451

452 453
        // Reset raw extent.
        this._rawExtent = {};
L
lang 已提交
454

455
        this._initDataFromProvider(0, data.count());
L
lang 已提交
456

457 458 459 460 461
        // If data has no item option.
        if (data.pure) {
            this.hasItemOption = false;
        }
    }
L
lang 已提交
462

463 464
    getProvider(): DataProvider {
        return this._rawData;
S
sushuang 已提交
465
    }
466 467

    /**
468
     * Caution: Can be only called on raw data (before `this._indices` created).
469
     */
470 471 472 473
    appendData(data: ArrayLike<any>): void {
        if (__DEV__) {
            zrUtil.assert(!this._indices, 'appendData can only be called on raw data.');
        }
S
sushuang 已提交
474

475 476
        const rawData = this._rawData;
        const start = this.count();
477
        rawData.appendData(data);
478
        let end = rawData.count();
479 480 481 482
        if (!rawData.persistent) {
            end += start;
        }
        this._initDataFromProvider(start, end);
483
    }
484

485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
    /**
     * Caution: Can be only called on raw data (before `this._indices` created).
     * This method does not modify `rawData` (`dataProvider`), but only
     * add values to storage.
     *
     * The final count will be increased by `Math.max(values.length, names.length)`.
     *
     * @param values That is the SourceType: 'arrayRows', like
     *        [
     *            [12, 33, 44],
     *            [NaN, 43, 1],
     *            ['-', 'asdf', 0]
     *        ]
     *        Each item is exaclty cooresponding to a dimension.
     */
    appendValues(values: any[][], names?: string[]): void {
501 502 503 504 505
        const chunkSize = this._chunkSize;
        const storage = this._storage;
        const dimensions = this.dimensions;
        const dimLen = dimensions.length;
        const rawExtent = this._rawExtent;
506

507 508 509
        const start = this.count();
        const end = start + Math.max(values.length, names ? names.length : 0);
        const originalChunkCount = this._chunkCount;
510

511
        for (let i = 0; i < dimLen; i++) {
512
            const dim = dimensions[i];
513 514 515 516 517 518 519 520
            if (!rawExtent[dim]) {
                rawExtent[dim] = getInitialExtent();
            }
            if (!storage[dim]) {
                storage[dim] = [];
            }
            prepareChunks(storage, this._dimensionInfos[dim], chunkSize, originalChunkCount, end);
            this._chunkCount = storage[dim].length;
S
sushuang 已提交
521
        }
522

523
        const emptyDataItem = new Array(dimLen);
524
        for (let idx = start; idx < end; idx++) {
525 526 527
            const sourceIdx = idx - start;
            const chunkIndex = Math.floor(idx / chunkSize);
            const chunkOffset = idx % chunkSize;
528 529

            // Store the data by dimensions
530
            for (let k = 0; k < dimLen; k++) {
531 532
                const dim = dimensions[k];
                const val = this._dimValueGetterArrayRows(
533
                    values[sourceIdx] || emptyDataItem, dim, sourceIdx, k
534
                ) as ParsedValueNumeric;
535 536
                storage[dim][chunkIndex][chunkOffset] = val;

537
                const dimRawExtent = rawExtent[dim];
538 539 540
                val < dimRawExtent[0] && (dimRawExtent[0] = val);
                val > dimRawExtent[1] && (dimRawExtent[1] = val);
            }
541

542 543 544
            if (names) {
                this._nameList[idx] = names[sourceIdx];
            }
545 546
        }

547
        this._rawCount = this._count = end;
548

549 550
        // Reset data extent
        this._extent = {};
551

552 553
        prepareInvertedIndex(this);
    }
554

555 556 557 558
    private _initDataFromProvider(start: number, end: number): void {
        if (start >= end) {
            return;
        }
559

560 561 562 563 564 565 566 567 568 569
        const chunkSize = this._chunkSize;
        const rawData = this._rawData;
        const storage = this._storage;
        const dimensions = this.dimensions;
        const dimLen = dimensions.length;
        const dimensionInfoMap = this._dimensionInfos;
        const nameList = this._nameList;
        const idList = this._idList;
        const rawExtent = this._rawExtent;
        const nameRepeatCount: NameRepeatCount = this._nameRepeatCount = {};
570 571
        let nameDimIdx;

572
        const originalChunkCount = this._chunkCount;
573
        for (let i = 0; i < dimLen; i++) {
574
            const dim = dimensions[i];
575 576 577
            if (!rawExtent[dim]) {
                rawExtent[dim] = getInitialExtent();
            }
S
sushuang 已提交
578

579
            const dimInfo = dimensionInfoMap[dim];
580 581 582 583 584 585
            if (dimInfo.otherDims.itemName === 0) {
                nameDimIdx = this._nameDimIdx = i;
            }
            if (dimInfo.otherDims.itemId === 0) {
                this._idDimIdx = i;
            }
S
sushuang 已提交
586

587 588 589
            if (!storage[dim]) {
                storage[dim] = [];
            }
S
tweak  
sushuang 已提交
590

591
            prepareChunks(storage, dimInfo, chunkSize, originalChunkCount, end);
592

593
            this._chunkCount = storage[dim].length;
S
sushuang 已提交
594
        }
595

596 597
        let dataItem = new Array(dimLen) as OptionDataItem;
        for (let idx = start; idx < end; idx++) {
598 599 600 601 602 603 604 605
            // NOTICE: Try not to write things into dataItem
            dataItem = rawData.getItem(idx, dataItem);
            // Each data item is value
            // [1, 2]
            // 2
            // Bar chart, line chart which uses category axis
            // only gives the 'y' value. 'x' value is the indices of category
            // Use a tempValue to normalize the value to be a (x, y) value
606 607
            const chunkIndex = Math.floor(idx / chunkSize);
            const chunkOffset = idx % chunkSize;
608 609

            // Store the data by dimensions
610
            for (let k = 0; k < dimLen; k++) {
611 612
                const dim = dimensions[k];
                const dimStorage = storage[dim][chunkIndex];
613
                // PENDING NULL is empty or zero
614
                const val = this._dimValueGetter(dataItem, dim, idx, k) as ParsedValueNumeric;
615 616
                dimStorage[chunkOffset] = val;

617
                const dimRawExtent = rawExtent[dim];
618 619 620
                val < dimRawExtent[0] && (dimRawExtent[0] = val);
                val > dimRawExtent[1] && (dimRawExtent[1] = val);
            }
S
sushuang 已提交
621

622 623 624
            // ??? FIXME not check by pure but sourceFormat?
            // TODO refactor these logic.
            if (!rawData.pure) {
625
                let name: any = nameList[idx];
626 627 628 629 630 631 632 633 634 635

                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 = (dataItem as any).name;
                    }
                    else if (nameDimIdx != null) {
636 637
                        const nameDim = dimensions[nameDimIdx];
                        const nameDimChunk = storage[nameDim][chunkIndex];
638 639
                        if (nameDimChunk) {
                            name = nameDimChunk[chunkOffset];
640
                            const ordinalMeta = dimensionInfoMap[nameDim].ordinalMeta;
641 642 643
                            if (ordinalMeta && ordinalMeta.categories.length) {
                                name = ordinalMeta.categories[name];
                            }
644 645 646
                        }
                    }
                }
S
sushuang 已提交
647

648 649
                // Try using the id in option
                // id or name is used on dynamical data, mapping old and new items.
650
                let id = dataItem == null ? null : (dataItem as any).id;
S
sushuang 已提交
651

652 653 654 655 656 657 658 659
                if (id == null && name != null) {
                    // Use name as id and add counter to avoid same name
                    nameRepeatCount[name] = nameRepeatCount[name] || 0;
                    id = name;
                    if (nameRepeatCount[name] > 0) {
                        id += '__ec__' + nameRepeatCount[name];
                    }
                    nameRepeatCount[name]++;
S
sushuang 已提交
660
                }
661
                id != null && (idList[idx] = id);
S
sushuang 已提交
662 663
            }
        }
S
sushuang 已提交
664

665 666 667 668
        if (!rawData.persistent && rawData.clean) {
            // Clean unused data if data source is typed array.
            rawData.clean();
        }
P
pissang 已提交
669

670
        this._rawCount = this._count = end;
S
sushuang 已提交
671

672 673
        // Reset data extent
        this._extent = {};
S
tweak  
sushuang 已提交
674

675
        prepareInvertedIndex(this);
676 677
    }

678 679
    count(): number {
        return this._count;
680 681
    }

682
    getIndices(): ArrayLike<number> {
683
        let newIndices;
S
sushuang 已提交
684

685
        const indices = this._indices;
686
        if (indices) {
687 688
            const Ctor = indices.constructor as DataArrayLikeConstructor;
            const thisCount = this._count;
689 690 691
            // `new Array(a, b, c)` is different from `new Uint32Array(a, b, c)`.
            if (Ctor === Array) {
                newIndices = new Ctor(thisCount);
1
100pah 已提交
692
                for (let i = 0; i < thisCount; i++) {
693 694
                    newIndices[i] = indices[i];
                }
S
sushuang 已提交
695
            }
696 697 698 699
            else {
                newIndices = new (Ctor as DataTypedArrayConstructor)(
                    (indices as DataTypedArray).buffer, 0, thisCount
                );
S
sushuang 已提交
700 701
            }
        }
702
        else {
703
            const Ctor = getIndicesCtor(this);
704
            newIndices = new Ctor(this.count());
1
100pah 已提交
705
            for (let i = 0; i < newIndices.length; i++) {
706
                newIndices[i] = i;
707
            }
708 709
        }

710 711
        return newIndices;
    }
712

713 714 715 716
    /**
     * Get value. Return NaN if idx is out of range.
     * @param dim Dim must be concrete name.
     */
717
    get(dim: DimensionName, idx: number): ParsedValue {
718 719
        if (!(idx >= 0 && idx < this._count)) {
            return NaN;
S
sushuang 已提交
720
        }
721
        const storage = this._storage;
722 723 724
        if (!storage[dim]) {
            // TODO Warn ?
            return NaN;
S
sushuang 已提交
725
        }
726 727 728

        idx = this.getRawIndex(idx);

729 730
        const chunkIndex = Math.floor(idx / this._chunkSize);
        const chunkOffset = idx % this._chunkSize;
731

732 733
        const chunkStore = storage[dim][chunkIndex];
        const value = chunkStore[chunkOffset];
734 735
        // FIXME ordinal data type is not stackable
        // if (stack) {
736
        //     let dimensionInfo = this._dimensionInfos[dim];
737
        //     if (dimensionInfo && dimensionInfo.stackable) {
738
        //         let stackedOn = this.stackedOn;
739 740
        //         while (stackedOn) {
        //             // Get no stacked data of stacked on
741
        //             let stackedValue = stackedOn.get(dim, idx);
742 743 744 745 746 747 748 749 750 751 752 753
        //             // Considering positive stack, negative stack and empty data
        //             if ((value >= 0 && stackedValue > 0)  // Positive stack
        //                 || (value <= 0 && stackedValue < 0) // Negative stack
        //             ) {
        //                 value += stackedValue;
        //             }
        //             stackedOn = stackedOn.stackedOn;
        //         }
        //     }
        // }

        return value;
754
    }
S
sushuang 已提交
755

756 757 758
    /**
     * @param dim concrete dim
     */
759
    getByRawIndex(dim: DimensionName, rawIdx: number): ParsedValue {
760 761 762
        if (!(rawIdx >= 0 && rawIdx < this._rawCount)) {
            return NaN;
        }
763
        const dimStore = this._storage[dim];
764 765 766 767
        if (!dimStore) {
            // TODO Warn ?
            return NaN;
        }
S
sushuang 已提交
768

769 770 771
        const chunkIndex = Math.floor(rawIdx / this._chunkSize);
        const chunkOffset = rawIdx % this._chunkSize;
        const chunkStore = dimStore[chunkIndex];
772
        return chunkStore[chunkOffset];
P
pissang 已提交
773
    }
774 775 776 777 778

    /**
     * FIXME Use `get` on chrome maybe slow(in filterSelf and selectRange).
     * Hack a much simpler _getFast
     */
779
    private _getFast(dim: DimensionName, rawIdx: number): ParsedValue {
780 781 782
        const chunkIndex = Math.floor(rawIdx / this._chunkSize);
        const chunkOffset = rawIdx % this._chunkSize;
        const chunkStore = this._storage[dim][chunkIndex];
783
        return chunkStore[chunkOffset];
784
    }
P
pissang 已提交
785

786 787 788 789
    /**
     * Get value for multi dimensions.
     * @param dimensions If ignored, using all dimensions.
     */
790
    getValues(idx: number): ParsedValue[];
791 792
    getValues(dimensions: readonly DimensionName[], idx: number): ParsedValue[];
    getValues(dimensions: readonly DimensionName[] | number, idx?: number): ParsedValue[] {
793
        const values = [];
794 795 796

        if (!zrUtil.isArray(dimensions)) {
            // stack = idx;
797
            idx = dimensions as number;
798 799
            dimensions = this.dimensions;
        }
P
pissang 已提交
800

801
        for (let i = 0, len = dimensions.length; i < len; i++) {
802 803
            values.push(this.get(dimensions[i], idx /*, stack */));
        }
S
sushuang 已提交
804

805
        return values;
S
sushuang 已提交
806
    }
807 808 809 810 811 812

    /**
     * If value is NaN. Inlcuding '-'
     * Only check the coord dimensions.
     */
    hasValue(idx: number): boolean {
813
        const dataDimsOnCoord = this._dimensionsSummary.dataDimsOnCoord;
814
        for (let i = 0, len = dataDimsOnCoord.length; i < len; i++) {
815 816 817 818 819 820 821 822
            // Ordinal type originally can be string or number.
            // But when an ordinal type is used on coord, it can
            // not be string but only number. So we can also use isNaN.
            if (isNaN(this.get(dataDimsOnCoord[i], idx) as any)) {
                return false;
            }
        }
        return true;
S
sushuang 已提交
823 824
    }

825 826 827 828 829 830
    /**
     * Get extent of data in one dimension
     */
    getDataExtent(dim: DimensionLoose): [number, number] {
        // Make sure use concrete dim as cache name.
        dim = this.getDimension(dim);
831 832
        const dimData = this._storage[dim];
        const initialExtent = getInitialExtent();
S
sushuang 已提交
833

834
        // stack = !!((stack || false) && this.getCalculationInfo(dim));
P
pissang 已提交
835

836 837 838
        if (!dimData) {
            return initialExtent;
        }
S
sushuang 已提交
839

840
        // Make more strict checkings to ensure hitting cache.
841
        const currEnd = this.count();
842 843
        // let cacheName = [dim, !!stack].join('_');
        // let cacheName = dim;
S
sushuang 已提交
844

845 846 847
        // Consider the most cases when using data zoom, `getDataExtent`
        // happened before filtering. We cache raw extent, which is not
        // necessary to be cleared and recalculated when restore data.
848
        const useRaw = !this._indices; // && !stack;
849
        let dimExtent: [number, number];
S
sushuang 已提交
850

851 852 853 854 855 856 857 858
        if (useRaw) {
            return this._rawExtent[dim].slice() as [number, number];
        }
        dimExtent = this._extent[dim];
        if (dimExtent) {
            return dimExtent.slice() as [number, number];
        }
        dimExtent = initialExtent;
S
sushuang 已提交
859

860 861
        let min = dimExtent[0];
        let max = dimExtent[1];
862

863 864
        for (let i = 0; i < currEnd; i++) {
            // let value = stack ? this.get(dim, i, true) : this._getFast(dim, this.getRawIndex(i));
865
            const value = this._getFast(dim, this.getRawIndex(i)) as ParsedValueNumeric;
866 867
            value < min && (min = value);
            value > max && (max = value);
L
lang 已提交
868
        }
L
lang 已提交
869

870
        dimExtent = [min, max];
S
sushuang 已提交
871

872
        this._extent[dim] = dimExtent;
P
pissang 已提交
873

874
        return dimExtent;
S
sushuang 已提交
875
    }
S
sushuang 已提交
876

877
    /**
878 879 880 881 882 883 884 885
     * PENDING: In fact currently this function is only used to short-circuit
     * the calling of `scale.unionExtentFromData` when data have been filtered by modules
     * like "dataZoom". `scale.unionExtentFromData` is used to calculate data extent for series on
     * an axis, but if a "axis related data filter module" is used, the extent of the axis have
     * been fixed and no need to calling `scale.unionExtentFromData` actually.
     * But if we add "custom data filter" in future, which is not "axis related", this method may
     * be still needed.
     *
886 887 888 889 890 891 892
     * Optimize for the scenario that data is filtered by a given extent.
     * Consider that if data amount is more than hundreds of thousand,
     * extent calculation will cost more than 10ms and the cache will
     * be erased because of the filtering.
     */
    getApproximateExtent(dim: DimensionLoose): [number, number] {
        dim = this.getDimension(dim);
893
        return this._approximateExtent[dim] || this.getDataExtent(dim);
894
    }
S
sushuang 已提交
895

896 897 898 899
    /**
     * Calculate extent on a filtered data might be time consuming.
     * Approximate extent is only used for: calculte extent of filtered data outside.
     */
900 901 902
    setApproximateExtent(extent: [number, number], dim: DimensionLoose): void {
        dim = this.getDimension(dim);
        this._approximateExtent[dim] = extent.slice() as [number, number];
S
sushuang 已提交
903
    }
904

905
    getCalculationInfo(key: string): any {
906
        return this._calculationInfo[key];
S
sushuang 已提交
907 908
    }

909 910 911 912 913 914 915 916
    /**
     * @param key or k-v object
     */
    setCalculationInfo(key: string | object, value?: any) {
        isObject(key)
            ? zrUtil.extend(this._calculationInfo, key as object)
            : (this._calculationInfo[key] = value);
    }
S
sushuang 已提交
917

918 919 920 921
    /**
     * Get sum of data in one dimension
     */
    getSum(dim: DimensionName): number {
922
        const dimData = this._storage[dim];
923
        let sum = 0;
924
        if (dimData) {
925
            for (let i = 0, len = this.count(); i < len; i++) {
926
                const value = this.get(dim, i) as number;
927 928 929 930 931 932
                if (!isNaN(value)) {
                    sum += value;
                }
            }
        }
        return sum;
S
sushuang 已提交
933
    }
S
sushuang 已提交
934

935 936 937 938
    /**
     * Get median of data in one dimension
     */
    getMedian(dim: DimensionLoose): number {
939
        const dimDataArray: ParsedValue[] = [];
940 941 942 943 944 945 946 947 948
        // map all data of one dimension
        this.each(dim, function (val) {
            if (!isNaN(val as number)) {
                dimDataArray.push(val);
            }
        });

        // TODO
        // Use quick select?
949
        const sortedDimDataArray = dimDataArray.sort(function (a: number, b: number) {
950
            return a - b;
951
        }) as number[];
952
        const len = this.count();
953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970
        // calculate median
        return len === 0
            ? 0
            : len % 2 === 1
            ? sortedDimDataArray[(len - 1) / 2]
            : (sortedDimDataArray[len / 2] + sortedDimDataArray[len / 2 - 1]) / 2;
    }

    // /**
    //  * Retreive the index with given value
    //  * @param {string} dim Concrete dimension.
    //  * @param {number} value
    //  * @return {number}
    //  */
    // Currently incorrect: should return dataIndex but not rawIndex.
    // Do not fix it until this method is to be used somewhere.
    // FIXME Precision of float value
    // indexOf(dim, value) {
971 972 973
    //     let storage = this._storage;
    //     let dimData = storage[dim];
    //     let chunkSize = this._chunkSize;
974
    //     if (dimData) {
975 976 977
    //         for (let i = 0, len = this.count(); i < len; i++) {
    //             let chunkIndex = Math.floor(i / chunkSize);
    //             let chunkOffset = i % chunkSize;
978 979 980 981 982 983 984
    //             if (dimData[chunkIndex][chunkOffset] === value) {
    //                 return i;
    //             }
    //         }
    //     }
    //     return -1;
    // }
S
sushuang 已提交
985

986 987 988 989 990 991 992
    /**
     * Only support the dimension which inverted index created.
     * Do not support other cases until required.
     * @param dim concrete dim
     * @param value ordinal index
     * @return rawIndex
     */
1
100pah 已提交
993
    rawIndexOf(dim: DimensionName, value: OrdinalNumber): number {
994
        const invertedIndices = dim && this._invertedIndicesMap[dim];
995 996 997 998 999
        if (__DEV__) {
            if (!invertedIndices) {
                throw new Error('Do not supported yet');
            }
        }
1000
        const rawIndex = invertedIndices[value];
1001 1002 1003 1004 1005
        if (rawIndex == null || isNaN(rawIndex)) {
            return INDEX_NOT_FOUND;
        }
        return rawIndex;
    }
S
sushuang 已提交
1006

1007 1008 1009 1010
    /**
     * Retreive the index with given name
     */
    indexOfName(name: string): number {
1011
        for (let i = 0, len = this.count(); i < len; i++) {
1012 1013
            if (this.getName(i) === name) {
                return i;
L
lang 已提交
1014 1015
            }
        }
H
hustcc 已提交
1016

1017 1018
        return -1;
    }
S
sushuang 已提交
1019

1020 1021 1022 1023 1024 1025
    /**
     * Retreive the index with given raw data index
     */
    indexOfRawIndex(rawIndex: number): number {
        if (rawIndex >= this._rawCount || rawIndex < 0) {
            return -1;
L
lang 已提交
1026 1027
        }

1028 1029
        if (!this._indices) {
            return rawIndex;
1030 1031
        }

1032
        // Indices are ascending
1033
        const indices = this._indices;
L
lang 已提交
1034

1035
        // If rawIndex === dataIndex
1036
        const rawDataIndex = indices[rawIndex];
1037 1038 1039 1040
        if (rawDataIndex != null && rawDataIndex < this._count && rawDataIndex === rawIndex) {
            return rawIndex;
        }

1041 1042
        let left = 0;
        let right = this._count - 1;
1043
        while (left <= right) {
1044
            const mid = (left + right) / 2 | 0;
1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
            if (indices[mid] < rawIndex) {
                left = mid + 1;
            }
            else if (indices[mid] > rawIndex) {
                right = mid - 1;
            }
            else {
                return mid;
            }
        }
1055 1056 1057
        return -1;
    }

1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068
    /**
     * Retreive the index of nearest value
     * @param dim
     * @param value
     * @param [maxDistance=Infinity]
     * @return If and only if multiple indices has
     *         the same value, they are put to the result.
     */
    indicesOfNearest(
        dim: DimensionName, value: number, maxDistance?: number
    ): number[] {
1069 1070 1071
        const storage = this._storage;
        const dimData = storage[dim];
        const nearestIndices: number[] = [];
1072 1073 1074 1075

        if (!dimData) {
            return nearestIndices;
        }
1076

1077 1078 1079
        if (maxDistance == null) {
            maxDistance = Infinity;
        }
S
sushuang 已提交
1080

1081 1082 1083
        let minDist = Infinity;
        let minDiff = -1;
        let nearestIndicesLen = 0;
1084

1085
        // Check the test case of `test/ut/spec/data/List.js`.
1086
        for (let i = 0, len = this.count(); i < len; i++) {
1087 1088
            const diff = value - (this.get(dim, i) as number);
            const dist = Math.abs(diff);
1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106
            if (dist <= maxDistance) {
                // When the `value` is at the middle of `this.get(dim, i)` and `this.get(dim, i+1)`,
                // we'd better not push both of them to `nearestIndices`, otherwise it is easy to
                // get more than one item in `nearestIndices` (more specifically, in `tooltip`).
                // So we chose the one that `diff >= 0` in this csae.
                // But if `this.get(dim, i)` and `this.get(dim, j)` get the same value, both of them
                // should be push to `nearestIndices`.
                if (dist < minDist
                    || (dist === minDist && diff >= 0 && minDiff < 0)
                ) {
                    minDist = dist;
                    minDiff = diff;
                    nearestIndicesLen = 0;
                }
                if (diff === minDiff) {
                    nearestIndices[nearestIndicesLen++] = i;
                }
            }
L
lang 已提交
1107
        }
1108
        nearestIndices.length = nearestIndicesLen;
L
lang 已提交
1109

S
sushuang 已提交
1110 1111
        return nearestIndices;
    }
L
lang 已提交
1112

1113 1114 1115 1116 1117 1118
    /**
     * Get raw data index.
     * Do not initialize.
     * Default `getRawIndex`. And it can be changed.
     */
    getRawIndex: (idx: number) => number = getRawIndexWithoutIndices;
L
lang 已提交
1119

1120 1121 1122 1123 1124
    /**
     * Get raw data item
     */
    getRawDataItem(idx: number): OptionDataItem {
        if (!this._rawData.persistent) {
1125
            const val = [];
1126
            for (let i = 0; i < this.dimensions.length; i++) {
1127
                const dim = this.dimensions[i];
1128
                val.push(this.get(dim, idx));
S
sushuang 已提交
1129
            }
1130 1131 1132 1133
            return val;
        }
        else {
            return this._rawData.getItem(this.getRawIndex(idx));
L
lang 已提交
1134 1135
        }
    }
P
pissang 已提交
1136

1137
    getName(idx: number): string {
1138
        const rawIndex = this.getRawIndex(idx);
1139 1140 1141 1142
        return this._nameList[rawIndex]
            || getRawValueFromStore(this, this._nameDimIdx, rawIndex)
            || '';
    }
P
pissang 已提交
1143

1144 1145
    getId(idx: number): string {
        return getId(this, this.getRawIndex(idx));
1146
    }
L
lang 已提交
1147

1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170
    /**
     * Data iteration
     * @param ctx default this
     * @example
     *  list.each('x', function (x, idx) {});
     *  list.each(['x', 'y'], function (x, y, idx) {});
     *  list.each(function (idx) {})
     */
    each<Ctx>(cb: EachCb0<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void;
    each<Ctx>(dims: DimensionLoose, cb: EachCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void;
    each<Ctx>(dims: [DimensionLoose], cb: EachCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void;
    each<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: EachCb2<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void;
    each<Ctx>(dims: ItrParamDims, cb: EachCb<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): void;
    each<Ctx>(
        dims: ItrParamDims | EachCb<Ctx>,
        cb: EachCb<Ctx> | Ctx,
        ctx?: Ctx,
        ctxCompat?: Ctx
    ): void {
        'use strict';

        if (!this._count) {
            return;
1171
        }
L
lang 已提交
1172

1173 1174 1175 1176 1177 1178
        if (typeof dims === 'function') {
            ctxCompat = ctx;
            ctx = cb as Ctx;
            cb = dims;
            dims = [];
        }
L
lang 已提交
1179

1180
        // ctxCompat just for compat echarts3
1181
        const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>;
L
lang 已提交
1182

1183
        const dimNames = zrUtil.map(normalizeDimensions(dims), this.getDimension, this);
L
lang 已提交
1184

1185 1186 1187
        if (__DEV__) {
            validateDimensions(this, dimNames);
        }
L
lang 已提交
1188

1189
        const dimSize = dimNames.length;
1190

1191
        for (let i = 0; i < this.count(); i++) {
1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203
            // Simple optimization
            switch (dimSize) {
                case 0:
                    (cb as EachCb0<Ctx>).call(fCtx, i);
                    break;
                case 1:
                    (cb as EachCb1<Ctx>).call(fCtx, this.get(dimNames[0], i), i);
                    break;
                case 2:
                    (cb as EachCb2<Ctx>).call(fCtx, this.get(dimNames[0], i), this.get(dimNames[1], i), i);
                    break;
                default:
1204
                    let k = 0;
1205
                    const value = [];
1206 1207 1208 1209 1210 1211 1212
                    for (; k < dimSize; k++) {
                        value[k] = this.get(dimNames[k], i);
                    }
                    // Index
                    value[k] = i;
                    (cb as EachCb<Ctx>).apply(fCtx, value);
            }
1213 1214 1215
        }
    }

1216 1217 1218
    /**
     * Data filter
     */
P
pissang 已提交
1219 1220 1221 1222 1223
    filterSelf<Ctx>(cb: FilterCb0<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): this;
    filterSelf<Ctx>(dims: DimensionLoose, cb: FilterCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): this;
    filterSelf<Ctx>(dims: [DimensionLoose], cb: FilterCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): this;
    filterSelf<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: FilterCb2<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): this;
    filterSelf<Ctx>(dims: ItrParamDims, cb: FilterCb<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): this;
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234
    filterSelf<Ctx>(
        dims: ItrParamDims | FilterCb<Ctx>,
        cb: FilterCb<Ctx> | Ctx,
        ctx?: Ctx,
        ctxCompat?: Ctx
    ): List {
        'use strict';

        if (!this._count) {
            return;
        }
1235

1236 1237 1238 1239 1240 1241
        if (typeof dims === 'function') {
            ctxCompat = ctx;
            ctx = cb as Ctx;
            cb = dims;
            dims = [];
        }
P
pissang 已提交
1242

1243
        // ctxCompat just for compat echarts3
1244
        const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>;
S
sushuang 已提交
1245

1246
        const dimNames = zrUtil.map(
1247 1248
            normalizeDimensions(dims), this.getDimension, this
        );
S
sushuang 已提交
1249

1250 1251 1252
        if (__DEV__) {
            validateDimensions(this, dimNames);
        }
S
sushuang 已提交
1253

1254

1255 1256 1257 1258 1259
        const count = this.count();
        const Ctor = getIndicesCtor(this);
        const newIndices = new Ctor(count);
        const value = [];
        const dimSize = dimNames.length;
1260

1261
        let offset = 0;
1262
        const dim0 = dimNames[0];
1263

1264 1265
        for (let i = 0; i < count; i++) {
            let keep;
1266
            const rawIdx = this.getRawIndex(i);
1267 1268 1269 1270 1271
            // Simple optimization
            if (dimSize === 0) {
                keep = (cb as FilterCb0<Ctx>).call(fCtx, i);
            }
            else if (dimSize === 1) {
1272
                const val = this._getFast(dim0, rawIdx);
1273 1274 1275
                keep = (cb as FilterCb1<Ctx>).call(fCtx, val, i);
            }
            else {
1
100pah 已提交
1276 1277
                let k = 0;
                for (; k < dimSize; k++) {
1278
                    value[k] = this._getFast(dim0, rawIdx);
L
lang 已提交
1279 1280
                }
                value[k] = i;
1281 1282 1283 1284 1285
                keep = (cb as FilterCb<Ctx>).apply(fCtx, value);
            }
            if (keep) {
                newIndices[offset++] = rawIdx;
            }
L
lang 已提交
1286
        }
P
pissang 已提交
1287

1288 1289 1290 1291 1292 1293 1294
        // Set indices after filtered.
        if (offset < count) {
            this._indices = newIndices;
        }
        this._count = offset;
        // Reset data extent
        this._extent = {};
P
pissang 已提交
1295

1296
        this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;
S
tweak.  
sushuang 已提交
1297

1298
        return this;
1299 1300
    }

1301 1302 1303 1304
    /**
     * Select data in range. (For optimization of filter)
     * (Manually inline code, support 5 million data filtering in data zoom.)
     */
P
pissang 已提交
1305
    selectRange(range: {[dimName: string]: [number, number]}): List {
1306
        'use strict';
1307

1308 1309
        if (!this._count) {
            return;
S
tweak.  
sushuang 已提交
1310
        }
1311

1312 1313
        const dimensions = [];
        for (const dim in range) {
1314 1315
            if (range.hasOwnProperty(dim)) {
                dimensions.push(dim);
S
tweak.  
sushuang 已提交
1316 1317
            }
        }
P
pissang 已提交
1318

1319 1320
        if (__DEV__) {
            validateDimensions(this, dimensions);
S
sushuang 已提交
1321
        }
1322

1323
        const dimSize = dimensions.length;
1324 1325 1326
        if (!dimSize) {
            return;
        }
P
pissang 已提交
1327

1328 1329 1330
        const originalCount = this.count();
        const Ctor = getIndicesCtor(this);
        const newIndices = new Ctor(originalCount);
1331

1332
        let offset = 0;
1333
        const dim0 = dimensions[0];
1334

1335 1336
        const min = range[dim0][0];
        const max = range[dim0][1];
1337

1338
        let quickFinished = false;
1339 1340
        if (!this._indices) {
            // Extreme optimization for common case. About 2x faster in chrome.
1341
            let idx = 0;
1342
            if (dimSize === 1) {
1343
                const dimStorage = this._storage[dimensions[0]];
1
100pah 已提交
1344
                for (let k = 0; k < this._chunkCount; k++) {
1345 1346
                    const chunkStorage = dimStorage[k];
                    const len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
1
100pah 已提交
1347
                    for (let i = 0; i < len; i++) {
1348
                        const val = chunkStorage[i];
1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359
                        // NaN will not be filtered. Consider the case, in line chart, empty
                        // value indicates the line should be broken. But for the case like
                        // scatter plot, a data item with empty value will not be rendered,
                        // but the axis extent may be effected if some other dim of the data
                        // item has value. Fortunately it is not a significant negative effect.
                        if (
                            (val >= min && val <= max) || isNaN(val as any)
                        ) {
                            newIndices[offset++] = idx;
                        }
                        idx++;
1360 1361
                    }
                }
1362
                quickFinished = true;
P
pissang 已提交
1363
            }
1364
            else if (dimSize === 2) {
1365 1366 1367 1368
                const dimStorage = this._storage[dim0];
                const dimStorage2 = this._storage[dimensions[1]];
                const min2 = range[dimensions[1]][0];
                const max2 = range[dimensions[1]][1];
1
100pah 已提交
1369
                for (let k = 0; k < this._chunkCount; k++) {
1370 1371 1372
                    const chunkStorage = dimStorage[k];
                    const chunkStorage2 = dimStorage2[k];
                    const len = Math.min(this._count - k * this._chunkSize, this._chunkSize);
1
100pah 已提交
1373
                    for (let i = 0; i < len; i++) {
1374 1375
                        const val = chunkStorage[i];
                        const val2 = chunkStorage2[i];
1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386
                        // Do not filter NaN, see comment above.
                        if ((
                                (val >= min && val <= max) || isNaN(val as any)
                            )
                            && (
                                (val2 >= min2 && val2 <= max2) || isNaN(val2 as any)
                            )
                        ) {
                            newIndices[offset++] = idx;
                        }
                        idx++;
P
pissang 已提交
1387
                    }
P
pissang 已提交
1388
                }
1389
                quickFinished = true;
P
pissang 已提交
1390 1391
            }
        }
1392 1393
        if (!quickFinished) {
            if (dimSize === 1) {
1
100pah 已提交
1394
                for (let i = 0; i < originalCount; i++) {
1395 1396
                    const rawIndex = this.getRawIndex(i);
                    const val = this._getFast(dim0, rawIndex);
1397
                    // Do not filter NaN, see comment above.
1398 1399 1400 1401
                    if (
                        (val >= min && val <= max) || isNaN(val as any)
                    ) {
                        newIndices[offset++] = rawIndex;
P
pissang 已提交
1402 1403
                    }
                }
1404 1405
            }
            else {
1
100pah 已提交
1406 1407
                for (let i = 0; i < originalCount; i++) {
                    let keep = true;
1408
                    const rawIndex = this.getRawIndex(i);
1
100pah 已提交
1409
                    for (let k = 0; k < dimSize; k++) {
1410 1411
                        const dimk = dimensions[k];
                        const val = this._getFast(dimk, rawIndex);
1412 1413 1414 1415 1416 1417 1418 1419
                        // Do not filter NaN, see comment above.
                        if (val < range[dimk][0] || val > range[dimk][1]) {
                            keep = false;
                        }
                    }
                    if (keep) {
                        newIndices[offset++] = this.getRawIndex(i);
                    }
P
pissang 已提交
1420 1421 1422 1423
                }
            }
        }

1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434
        // Set indices after filtered.
        if (offset < originalCount) {
            this._indices = newIndices;
        }
        this._count = offset;
        // Reset data extent
        this._extent = {};

        this.getRawIndex = this._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;

        return this;
P
pissang 已提交
1435 1436
    }

1437 1438 1439
    /**
     * Data mapping to a plain array
     */
P
pissang 已提交
1440
    mapArray<Ctx, Cb extends MapArrayCb0<Ctx>>(cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
1441
    /* eslint-disable */
P
pissang 已提交
1442 1443 1444 1445
    mapArray<Ctx, Cb extends MapArrayCb1<Ctx>>(dims: DimensionLoose, cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
    mapArray<Ctx, Cb extends MapArrayCb1<Ctx>>(dims: [DimensionLoose], cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
    mapArray<Ctx, Cb extends MapArrayCb2<Ctx>>(dims: [DimensionLoose, DimensionLoose], cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
    mapArray<Ctx, Cb extends MapArrayCb<Ctx>>(dims: ItrParamDims, cb: Cb, ctx?: Ctx, ctxCompat?: Ctx): ReturnType<Cb>[];
1446
    /* eslint-enable */
1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460
    mapArray<Ctx>(
        dims: ItrParamDims | MapArrayCb<Ctx>,
        cb: MapArrayCb<Ctx> | Ctx,
        ctx?: Ctx,
        ctxCompat?: Ctx
    ): any[] {
        'use strict';

        if (typeof dims === 'function') {
            ctxCompat = ctx;
            ctx = cb as Ctx;
            cb = dims;
            dims = [];
        }
P
pissang 已提交
1461

1462 1463
        // ctxCompat just for compat echarts3
        ctx = (ctx || ctxCompat || this) as Ctx;
P
pissang 已提交
1464

1465
        const result: any[] = [];
1466 1467 1468 1469
        this.each(dims, function () {
            result.push(cb && (cb as MapArrayCb<Ctx>).apply(this, arguments));
        }, ctx);
        return result;
S
sushuang 已提交
1470 1471
    }

1472 1473 1474
    /**
     * Data mapping to a new List with given dimensions
     */
P
pissang 已提交
1475 1476 1477
    map<Ctx>(dims: DimensionLoose, cb: MapCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): List<HostModel>;
    map<Ctx>(dims: [DimensionLoose], cb: MapCb1<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): List<HostModel>;
    map<Ctx>(dims: [DimensionLoose, DimensionLoose], cb: MapCb2<Ctx>, ctx?: Ctx, ctxCompat?: Ctx): List<HostModel>;
1478 1479 1480 1481 1482 1483 1484 1485 1486
    map<Ctx>(
        dims: ItrParamDims,
        cb: MapCb<Ctx>,
        ctx?: Ctx,
        ctxCompat?: Ctx
    ): List {
        'use strict';

        // ctxCompat just for compat echarts3
1487
        const fCtx = (ctx || ctxCompat || this) as CtxOrList<Ctx>;
1488

1489
        const dimNames = zrUtil.map(
1490 1491 1492 1493 1494 1495 1496
            normalizeDimensions(dims), this.getDimension, this
        );

        if (__DEV__) {
            validateDimensions(this, dimNames);
        }

1497
        const list = cloneListForMapAndSample(this, dimNames);
S
sushuang 已提交
1498

1499 1500 1501 1502 1503
        // Following properties are all immutable.
        // So we can reference to the same value
        list._indices = this._indices;
        list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;

1504
        const storage = list._storage;
S
sushuang 已提交
1505

1506 1507 1508 1509 1510 1511
        const tmpRetValue = [];
        const chunkSize = this._chunkSize;
        const dimSize = dimNames.length;
        const dataCount = this.count();
        const values = [];
        const rawExtent = list._rawExtent;
1512

1513 1514
        for (let dataIndex = 0; dataIndex < dataCount; dataIndex++) {
            for (let dimIndex = 0; dimIndex < dimSize; dimIndex++) {
1515
                values[dimIndex] = this.get(dimNames[dimIndex], dataIndex);
S
sushuang 已提交
1516
            }
1517 1518
            values[dimSize] = dataIndex;

1519
            let retValue = cb && cb.apply(fCtx, values);
1520 1521 1522 1523 1524 1525 1526
            if (retValue != null) {
                // a number or string (in oridinal dimension)?
                if (typeof retValue !== 'object') {
                    tmpRetValue[0] = retValue;
                    retValue = tmpRetValue;
                }

1527 1528 1529
                const rawIndex = this.getRawIndex(dataIndex);
                const chunkIndex = Math.floor(rawIndex / chunkSize);
                const chunkOffset = rawIndex % chunkSize;
1530

1531
                for (let i = 0; i < retValue.length; i++) {
1532 1533 1534
                    const dim = dimNames[i];
                    const val = retValue[i];
                    const rawExtentOnDim = rawExtent[dim];
1535

1536
                    const dimStore = storage[dim];
1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547
                    if (dimStore) {
                        dimStore[chunkIndex][chunkOffset] = val;
                    }

                    if (val < rawExtentOnDim[0]) {
                        rawExtentOnDim[0] = val as number;
                    }
                    if (val > rawExtentOnDim[1]) {
                        rawExtentOnDim[1] = val as number;
                    }
                }
S
sushuang 已提交
1548
            }
S
sushuang 已提交
1549
        }
S
sushuang 已提交
1550

1551
        return list;
S
sushuang 已提交
1552 1553
    }

1554 1555 1556 1557 1558 1559 1560
    /**
     * Large data down sampling on given dimension
     * @param sampleIndex Sample index for name and id
     */
    downSample(
        dimension: DimensionName,
        rate: number,
1561 1562
        sampleValue: (frameValues: ArrayLike<ParsedValue>) => ParsedValueNumeric,
        sampleIndex: (frameValues: ArrayLike<ParsedValue>, value: ParsedValueNumeric) => number
P
pissang 已提交
1563
    ): List<HostModel> {
1564 1565
        const list = cloneListForMapAndSample(this, [dimension]);
        const targetStorage = list._storage;
1566

1567
        const frameValues = [];
1568
        let frameSize = Math.floor(1 / rate);
1569

1570 1571 1572 1573
        const dimStore = targetStorage[dimension];
        const len = this.count();
        const chunkSize = this._chunkSize;
        const rawExtentOnDim = list._rawExtent[dimension];
1574

1575
        const newIndices = new (getIndicesCtor(this))(len);
1576

1577 1578
        let offset = 0;
        for (let i = 0; i < len; i += frameSize) {
1579 1580 1581 1582 1583
            // Last frame
            if (frameSize > len - i) {
                frameSize = len - i;
                frameValues.length = frameSize;
            }
1584
            for (let k = 0; k < frameSize; k++) {
1585 1586 1587
                const dataIdx = this.getRawIndex(i + k);
                const originalChunkIndex = Math.floor(dataIdx / chunkSize);
                const originalChunkOffset = dataIdx % chunkSize;
1588 1589
                frameValues[k] = dimStore[originalChunkIndex][originalChunkOffset];
            }
1590 1591
            const value = sampleValue(frameValues);
            const sampleFrameIdx = this.getRawIndex(
1592 1593
                Math.min(i + sampleIndex(frameValues, value) || 0, len - 1)
            );
1594 1595
            const sampleChunkIndex = Math.floor(sampleFrameIdx / chunkSize);
            const sampleChunkOffset = sampleFrameIdx % chunkSize;
1596 1597
            // Only write value on the filtered data
            dimStore[sampleChunkIndex][sampleChunkOffset] = value;
S
sushuang 已提交
1598

1599 1600 1601 1602 1603 1604 1605 1606 1607
            if (value < rawExtentOnDim[0]) {
                rawExtentOnDim[0] = value;
            }
            if (value > rawExtentOnDim[1]) {
                rawExtentOnDim[1] = value;
            }

            newIndices[offset++] = sampleFrameIdx;
        }
1608

1609 1610
        list._count = offset;
        list._indices = newIndices;
S
sushuang 已提交
1611

1612
        list.getRawIndex = getRawIndexWithIndices;
S
sushuang 已提交
1613

P
pissang 已提交
1614
        return list as List<HostModel>;
1615 1616
    }

1617 1618 1619
    /**
     * Get model of one data item.
     */
P
pissang 已提交
1620
    // TODO: Type of data item
P
pissang 已提交
1621 1622 1623 1624
    getItemModel<ItemOpts extends unknown = unknown>(idx: number): Model<ItemOpts
        // Extract item option with value key. FIXME will cause incompatitable issue
        // Extract<HostModel['option']['data'][number], { value?: any }>
    > {
1625 1626
        const hostModel = this.hostModel;
        const dataItem = this.getRawDataItem(idx) as ModelOption;
1627
        return new Model(dataItem, hostModel, hostModel && hostModel.ecModel);
1628
    }
S
sushuang 已提交
1629

1630 1631 1632 1633
    /**
     * Create a data differ
     */
    diff(otherList: List): DataDiffer {
1634
        const thisList = this;
1635 1636 1637 1638

        return new DataDiffer(
            otherList ? otherList.getIndices() : [],
            this.getIndices(),
1
100pah 已提交
1639
            function (idx: number) {
1640 1641
                return getId(otherList, idx);
            },
1
100pah 已提交
1642
            function (idx: number) {
1643 1644 1645 1646
                return getId(thisList, idx);
            }
        );
    }
S
sushuang 已提交
1647

1648 1649 1650
    /**
     * Get visual property.
     */
1651 1652
    getVisual<K extends keyof Visual>(key: K): Visual[K] {
        const visual = this._visual as Visual;
1653 1654
        return visual && visual[key];
    }
S
sushuang 已提交
1655

1656 1657 1658 1659 1660 1661 1662 1663 1664
    /**
     * Set visual property
     *
     * @example
     *  setVisual('color', color);
     *  setVisual({
     *      'color': color
     *  });
     */
1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708
    setVisual<K extends keyof Visual>(key: K, val: Visual[K]): void;
    setVisual(kvObj: Partial<Visual>): void;
    setVisual(kvObj: string | Partial<Visual>, val?: any): void {
        this._visual = this._visual || {};
        if (isObject(kvObj)) {
            zrUtil.extend(this._visual, kvObj);
        }
        else {
            this._visual[kvObj as string] = val;
        }
    }

    /**
     * Get visual property of single data item
     */
    // eslint-disable-next-line
    getItemVisual<K extends keyof Visual>(idx: number, key: K): Visual[K] {
        const itemVisual = this._itemVisuals[idx] as Visual;
        const val = itemVisual && itemVisual[key];
        if (val == null) {
            // Use global visual property
            return this.getVisual(key);
        }
        return val;
    }

    /**
     * Make sure itemVisual property is unique
     */
    // TODO: use key to save visual to reduce memory.
    // eslint-disable-next-line
    ensureUniqueItemVisual<K extends keyof Visual>(idx: number, key: K): Visual[K] {
        const itemVisuals = this._itemVisuals;
        let itemVisual = itemVisuals[idx] as Visual;
        if (!itemVisual) {
            itemVisual = itemVisuals[idx] = {} as Visual;
        }
        let val = itemVisual[key];
        if (!val) {
            val = this.getVisual(key);

            // TODO Performance?
            if (zrUtil.isArray(val)) {
                val = val.slice() as unknown as Visual[K];
1709
            }
1710 1711 1712 1713 1714
            else if (isObject(val)) {
                val = zrUtil.extend({}, val);
            }

            itemVisual[key] = val;
S
sushuang 已提交
1715
        }
1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752
        return val;
    }
    /**
     * Set visual property of single data item
     *
     * @param {number} idx
     * @param {string|Object} key
     * @param {*} [value]
     *
     * @example
     *  setItemVisual(0, 'color', color);
     *  setItemVisual(0, {
     *      'color': color
     *  });
     */
    // eslint-disable-next-line
    setItemVisual<K extends keyof Visual>(idx: number, key: K, value: Visual[K]): void;
    setItemVisual(idx: number, kvObject: Partial<Visual>): void;
    // eslint-disable-next-line
    setItemVisual<K extends keyof Visual>(idx: number, key: K | Partial<Visual>, value?: Visual[K]): void {
        const itemVisual = this._itemVisuals[idx] || {};
        this._itemVisuals[idx] = itemVisual;

        if (isObject(key)) {
            zrUtil.extend(itemVisual, key);
        }
        else {
            itemVisual[key as string] = value;
        }
    }

    /**
     * Clear itemVisuals and list visual.
     */
    clearAllVisual(): void {
        this._visual = {};
        this._itemVisuals = [];
1753
    }
S
sushuang 已提交
1754

1755 1756 1757 1758 1759 1760
    /**
     * Set layout property.
     */
    setLayout(key: string, val: any): void;
    setLayout(kvObj: Dictionary<any>): void;
    setLayout(key: string | Dictionary<any>, val?: any): void {
1761
        if (isObject(key)) {
1762
            for (const name in key) {
1763 1764 1765
                if (key.hasOwnProperty(name)) {
                    this.setLayout(name, key[name]);
                }
L
lang 已提交
1766
            }
1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777
            return;
        }
        this._layout[key] = val;
    }

    /**
     * Get layout property.
     */
    getLayout(key: string): any {
        return this._layout[key];
    }
S
sushuang 已提交
1778

1779 1780 1781
    /**
     * Get layout of single data item
     */
P
pissang 已提交
1782
    getItemLayout(idx: number): any {
1783 1784
        return this._itemLayouts[idx];
    }
S
sushuang 已提交
1785

1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797
    /**
     * Set layout of single data item
     */
    setItemLayout<M = false>(
        idx: number,
        layout: (M extends true ? Dictionary<any> : any),
        merge?: M
    ): void {
        this._itemLayouts[idx] = merge
            ? zrUtil.extend(this._itemLayouts[idx] || {}, layout)
            : layout;
    }
S
sushuang 已提交
1798

1799 1800 1801 1802 1803 1804
    /**
     * Clear all layout of single data item
     */
    clearItemLayouts(): void {
        this._itemLayouts.length = 0;
    }
S
sushuang 已提交
1805

1806 1807 1808 1809
    /**
     * Set graphic element relative to data. It can be set as null
     */
    setItemGraphicEl(idx: number, el: Element): void {
1810
        const hostModel = this.hostModel;
P
pissang 已提交
1811

1812
        if (el) {
1813
            const ecData = getECData(el);
1814 1815
            // Add data index and series index for indexing the data by element
            // Useful in tooltip
1816 1817 1818
            ecData.dataIndex = idx;
            ecData.dataType = this.dataType;
            ecData.seriesIndex = hostModel && (hostModel as any).seriesIndex;
1819 1820

            // TODO: not store dataIndex on children.
1821 1822 1823
            if (el.type === 'group') {
                el.traverse(setItemDataAndSeriesIndex, el);
            }
S
sushuang 已提交
1824 1825
        }

1826
        this._graphicEls[idx] = el;
S
sushuang 已提交
1827
    }
L
lang 已提交
1828

1829 1830 1831
    getItemGraphicEl(idx: number): Element {
        return this._graphicEls[idx];
    }
P
pissang 已提交
1832

1833 1834 1835 1836 1837 1838 1839 1840 1841 1842
    eachItemGraphicEl<Ctx = unknown>(
        cb: (this: Ctx, el: Element, idx: number) => void,
        context?: Ctx
    ): void {
        zrUtil.each(this._graphicEls, function (el, idx) {
            if (el) {
                cb && cb.call(context, el, idx);
            }
        });
    }
P
pissang 已提交
1843

1844 1845 1846 1847
    /**
     * Shallow clone a new list except visual and layout properties, and graph elements.
     * New list only change the indices.
     */
P
pissang 已提交
1848
    cloneShallow(list?: List<HostModel>): List<HostModel> {
1849
        if (!list) {
1850
            const dimensionInfoList = zrUtil.map(this.dimensions, this.getDimensionInfo, this);
1851 1852
            list = new List(dimensionInfoList, this.hostModel);
        }
L
lang 已提交
1853

1854 1855
        // FIXME
        list._storage = this._storage;
S
sushuang 已提交
1856

1857
        transferProperties(list, this);
S
sushuang 已提交
1858

1859 1860
        // Clone will not change the data extent and indices
        if (this._indices) {
1861
            const Ctor = this._indices.constructor as DataArrayLikeConstructor;
1862
            if (Ctor === Array) {
1863
                const thisCount = this._indices.length;
1864
                list._indices = new Ctor(thisCount);
1865
                for (let i = 0; i < thisCount; i++) {
1866 1867 1868 1869 1870
                    list._indices[i] = this._indices[i];
                }
            }
            else {
                list._indices = new (Ctor as DataTypedArrayConstructor)(this._indices);
L
lang 已提交
1871
            }
L
lang 已提交
1872
        }
1873 1874 1875 1876 1877 1878
        else {
            list._indices = null;
        }
        list.getRawIndex = list._indices ? getRawIndexWithIndices : getRawIndexWithoutIndices;

        return list;
S
sushuang 已提交
1879
    }
L
lang 已提交
1880

1881 1882 1883 1884 1885 1886 1887
    /**
     * Wrap some method to add more feature
     */
    wrapMethod(
        methodName: FunctionPropertyNames<List>,
        injectFunction: (...args: any) => any
    ): void {
1888
        const originalMethod = this[methodName];
1889 1890
        if (typeof originalMethod !== 'function') {
            return;
1891
        }
1892 1893 1894
        this.__wrappedMethods = this.__wrappedMethods || [];
        this.__wrappedMethods.push(methodName);
        this[methodName] = function () {
1895
            const res = (originalMethod as any).apply(this, arguments);
1896 1897
            return injectFunction.apply(this, [res].concat(zrUtil.slice(arguments)));
        };
S
sushuang 已提交
1898
    }
D
deqingli 已提交
1899 1900


1901 1902 1903
    // ----------------------------------------------------------
    // A work around for internal method visiting private member.
    // ----------------------------------------------------------
P
pissang 已提交
1904
    private static internalField = (function () {
L
lang 已提交
1905

1906
        defaultDimValueGetters = {
1907

1908
            arrayRows: getDimValueSimply,
L
lang 已提交
1909

1910 1911
            objectRows: function (
                this: List, dataItem: Dictionary<any>, dimName: string, dataIndex: number, dimIndex: number
1912
            ): ParsedValue {
1913 1914
                return convertDataValue(dataItem[dimName], this._dimensionInfos[dimName]);
            },
L
lang 已提交
1915

1916 1917 1918 1919
            keyedColumns: getDimValueSimply,

            original: function (
                this: List, dataItem: any, dimName: string, dataIndex: number, dimIndex: number
1920
            ): ParsedValue {
1921
                // Performance sensitive, do not use modelUtil.getDataItemValue.
1922
                // If dataItem is an plain object with no value field, the let `value`
1923 1924
                // will be assigned with the object, but it will be tread correctly
                // in the `convertDataValue`.
1925
                const value = dataItem && (dataItem.value == null ? dataItem : dataItem.value);
1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941

                // If any dataItem is like { value: 10 }
                if (!this._rawData.pure && isDataItemOption(dataItem)) {
                    this.hasItemOption = true;
                }
                return convertDataValue(
                    (value instanceof Array)
                        ? value[dimIndex]
                        // If value is a single number or something else not array.
                        : value,
                    this._dimensionInfos[dimName]
                );
            },

            typedArray: function (
                this: List, dataItem: any, dimName: string, dataIndex: number, dimIndex: number
1942
            ): ParsedValue {
1943
                return dataItem[dimIndex];
L
lang 已提交
1944
            }
P
pah100 已提交
1945

1946
        };
S
sushuang 已提交
1947

1948 1949
        function getDimValueSimply(
            this: List, dataItem: any, dimName: string, dataIndex: number, dimIndex: number
1950
        ): ParsedValue {
1951
            return convertDataValue(dataItem[dimIndex], this._dimensionInfos[dimName]);
P
pah100 已提交
1952
        }
1953

1954 1955 1956 1957 1958
        /**
         * Convert raw the value in to inner value in List.
         * [Caution]: this is the key logic of user value parser.
         * For backward compatibiliy, do not modify it until have to.
         */
1959
        function convertDataValue(value: any, dimInfo: DataDimensionInfo): ParsedValue {
1960
            // Performance sensitive.
1961
            const dimType = dimInfo && dimInfo.type;
1962 1963
            if (dimType === 'ordinal') {
                // If given value is a category string
1964
                const ordinalMeta = dimInfo && dimInfo.ordinalMeta;
1965 1966 1967 1968
                return ordinalMeta
                    ? ordinalMeta.parseAndCollect(value)
                    : value;
            }
P
pah100 已提交
1969

1970 1971 1972 1973 1974 1975 1976 1977
            if (dimType === 'time'
                // spead up when using timestamp
                && typeof value !== 'number'
                && value != null
                && value !== '-'
            ) {
                value = +parseDate(value);
            }
L
lang 已提交
1978

1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989
            // dimType defaults 'number'.
            // If dimType is not ordinal and value is null or undefined or NaN or '-',
            // parse to NaN.
            return (value == null || value === '')
                ? NaN
                // If string (like '-'), using '+' parse to NaN
                // If object, also parse to NaN
                : +value;
        };

        prepareInvertedIndex = function (list: List): void {
1990
            const invertedIndicesMap = list._invertedIndicesMap;
1991
            zrUtil.each(invertedIndicesMap, function (invertedIndices, dim) {
1992
                const dimInfo = list._dimensionInfos[dim];
1993 1994

                // Currently, only dimensions that has ordinalMeta can create inverted indices.
1995
                const ordinalMeta = dimInfo.ordinalMeta;
1996 1997 1998 1999 2000 2001
                if (ordinalMeta) {
                    invertedIndices = invertedIndicesMap[dim] = new CtorInt32Array(
                        ordinalMeta.categories.length
                    );
                    // The default value of TypedArray is 0. To avoid miss
                    // mapping to 0, we should set it as INDEX_NOT_FOUND.
1
100pah 已提交
2002
                    for (let i = 0; i < invertedIndices.length; i++) {
2003 2004
                        invertedIndices[i] = INDEX_NOT_FOUND;
                    }
1
100pah 已提交
2005
                    for (let i = 0; i < list._count; i++) {
2006 2007 2008 2009 2010 2011 2012 2013
                        // Only support the case that all values are distinct.
                        invertedIndices[list.get(dim, i) as number] = i;
                    }
                }
            });
        };

        getRawValueFromStore = function (list: List, dimIndex: number, rawIndex: number): any {
2014
            let val;
2015
            if (dimIndex != null) {
2016 2017 2018 2019 2020
                const chunkSize = list._chunkSize;
                const chunkIndex = Math.floor(rawIndex / chunkSize);
                const chunkOffset = rawIndex % chunkSize;
                const dim = list.dimensions[dimIndex];
                const chunk = list._storage[dim][chunkIndex];
2021 2022
                if (chunk) {
                    val = chunk[chunkOffset];
2023
                    const ordinalMeta = list._dimensionInfos[dim].ordinalMeta;
2024
                    if (ordinalMeta && ordinalMeta.categories.length) {
1
100pah 已提交
2025
                        val = ordinalMeta.categories[val as OrdinalNumber];
2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043
                    }
                }
            }
            return val;
        };

        getIndicesCtor = function (list: List): DataArrayLikeConstructor {
            // The possible max value in this._indicies is always this._rawCount despite of filtering.
            return list._rawCount > 65535 ? CtorUint32Array : CtorUint16Array;
        };

        prepareChunks = function (
            storage: DataStorage,
            dimInfo: DataDimensionInfo,
            chunkSize: number,
            chunkCount: number,
            end: number
        ): void {
2044 2045 2046 2047
            const DataCtor = dataCtors[dimInfo.type];
            const lastChunkIndex = chunkCount - 1;
            const dim = dimInfo.name;
            const resizeChunkArray = storage[dim][lastChunkIndex];
2048
            if (resizeChunkArray && resizeChunkArray.length < chunkSize) {
2049
                const newStore = new DataCtor(Math.min(end - lastChunkIndex * chunkSize, chunkSize));
2050 2051
                // The cost of the copy is probably inconsiderable
                // within the initial chunkSize.
2052
                for (let j = 0; j < resizeChunkArray.length; j++) {
2053 2054 2055 2056 2057 2058
                    newStore[j] = resizeChunkArray[j];
                }
                storage[dim][lastChunkIndex] = newStore;
            }

            // Create new chunks.
2059
            for (let k = chunkCount * chunkSize; k < end; k += chunkSize) {
2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075
                storage[dim].push(new DataCtor(Math.min(end - k, chunkSize)));
            }
        };

        getRawIndexWithoutIndices = function (this: List, idx: number): number {
            return idx;
        };

        getRawIndexWithIndices = function (this: List, idx: number): number {
            if (idx < this._count && idx >= 0) {
                return this._indices[idx];
            }
            return -1;
        };

        getId = function (list: List, rawIndex: number): string {
2076
            let id = list._idList[rawIndex];
2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096
            if (id == null) {
                id = getRawValueFromStore(list, list._idDimIdx, rawIndex);
            }
            if (id == null) {
                // FIXME Check the usage in graph, should not use prefix.
                id = ID_PREFIX + rawIndex;
            }
            return id;
        };

        normalizeDimensions = function (
            dimensions: ItrParamDims
        ): Array<DimensionLoose> {
            if (!zrUtil.isArray(dimensions)) {
                dimensions = [dimensions];
            }
            return dimensions;
        };

        validateDimensions = function (list: List, dims: DimensionName[]): void {
2097
            for (let i = 0; i < dims.length; i++) {
2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109
                // stroage may be empty when no data, so use
                // dimensionInfos to check.
                if (!list._dimensionInfos[dims[i]]) {
                    console.error('Unkown dimension ' + dims[i]);
                }
            }
        };

        // Data in excludeDimensions is copied, otherwise transfered.
        cloneListForMapAndSample = function (
            original: List, excludeDimensions: DimensionName[]
        ): List {
2110 2111
            const allDimensions = original.dimensions;
            const list = new List(
2112 2113 2114 2115 2116 2117
                zrUtil.map(allDimensions, original.getDimensionInfo, original),
                original.hostModel
            );
            // FIXME If needs stackedOn, value may already been stacked
            transferProperties(list, original);

2118 2119
            const storage = list._storage = {} as DataStorage;
            const originalStorage = original._storage;
2120 2121

            // Init storage
2122
            for (let i = 0; i < allDimensions.length; i++) {
2123
                const dim = allDimensions[i];
2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141
                if (originalStorage[dim]) {
                    // Notice that we do not reset invertedIndicesMap here, becuase
                    // there is no scenario of mapping or sampling ordinal dimension.
                    if (zrUtil.indexOf(excludeDimensions, dim) >= 0) {
                        storage[dim] = cloneDimStore(originalStorage[dim]);
                        list._rawExtent[dim] = getInitialExtent();
                        list._extent[dim] = null;
                    }
                    else {
                        // Direct reference for other dimensions
                        storage[dim] = originalStorage[dim];
                    }
                }
            }
            return list;
        };

        cloneDimStore = function (originalDimStore: DataValueChunk[]): DataValueChunk[] {
2142
            const newDimStore = new Array(originalDimStore.length);
2143
            for (let j = 0; j < originalDimStore.length; j++) {
2144 2145 2146 2147 2148 2149
                newDimStore[j] = cloneChunk(originalDimStore[j]);
            }
            return newDimStore;
        };

        function cloneChunk(originalChunk: DataValueChunk): DataValueChunk {
2150
            const Ctor = originalChunk.constructor;
2151 2152
            // Only shallow clone is enough when Array.
            return Ctor === Array
2153
                ? (originalChunk as Array<ParsedValue>).slice()
2154
                : new (Ctor as DataTypedArrayConstructor)(originalChunk as DataTypedArray);
S
sushuang 已提交
2155
        }
L
lang 已提交
2156

2157 2158 2159 2160 2161
        getInitialExtent = function (): [number, number] {
            return [Infinity, -Infinity];
        };

        setItemDataAndSeriesIndex = function (this: Element, child: Element): void {
2162 2163
            const childECData = getECData(child);
            const thisECData = getECData(this);
2164 2165 2166
            childECData.seriesIndex = thisECData.seriesIndex;
            childECData.dataIndex = thisECData.dataIndex;
            childECData.dataType = thisECData.dataType;
2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177
        };

        transferProperties = function (target: List, source: List): void {
            zrUtil.each(
                TRANSFERABLE_PROPERTIES.concat(source.__wrappedMethods || []),
                function (propName) {
                    if (source.hasOwnProperty(propName)) {
                        (target as any)[propName] = (source as any)[propName];
                    }
                }
            );
L
lang 已提交
2178

2179
            target.__wrappedMethods = source.__wrappedMethods;
L
lang 已提交
2180

2181 2182 2183
            zrUtil.each(CLONE_PROPERTIES, function (propName) {
                (target as any)[propName] = zrUtil.clone((source as any)[propName]);
            });
L
lang 已提交
2184

2185 2186
            target._calculationInfo = zrUtil.extend({}, source._calculationInfo);
        };
P
pah100 已提交
2187

2188
    })();
L
lang 已提交
2189

2190 2191
}

S
sushuang 已提交
2192
export default List;