List.js 29.8 KB
Newer Older
L
lang 已提交
1 2 3 4 5 6 7
/**
 * List for data storage
 * @module echarts/data/List
 */
define(function (require) {

    var UNDEFINED = 'undefined';
L
lang 已提交
8 9 10 11 12
    var globalObj = typeof window === 'undefined' ? global : window;
    var Float64Array = typeof globalObj.Float64Array === UNDEFINED
        ? Array : globalObj.Float64Array;
    var Int32Array = typeof globalObj.Int32Array === UNDEFINED
        ? Array : globalObj.Int32Array;
L
lang 已提交
13 14

    var dataCtors = {
15
        'float': Float64Array,
P
pah100 已提交
16
        'int': Int32Array,
L
lang 已提交
17
        // Ordinal data type can be string or int
P
pah100 已提交
18
        'ordinal': Array,
19 20
        'number': Array,
        'time': Array
L
lang 已提交
21
    };
L
lang 已提交
22

L
lang 已提交
23
    var Model = require('../model/Model');
L
lang 已提交
24
    var DataDiffer = require('./DataDiffer');
L
lang 已提交
25

L
lang 已提交
26
    var zrUtil = require('zrender/core/util');
P
pah100 已提交
27
    var modelUtil = require('../util/model');
L
lang 已提交
28
    var isObject = zrUtil.isObject;
L
lang 已提交
29

L
lang 已提交
30
    var IMMUTABLE_PROPERTIES = [
L
lang 已提交
31
        'stackedOn', '_nameList', '_idList', '_rawData'
L
lang 已提交
32 33
    ];

34 35
    var transferImmuProperties = function (a, b, wrappedMethod) {
        zrUtil.each(IMMUTABLE_PROPERTIES.concat(wrappedMethod || []), function (propName) {
36 37 38
            if (b.hasOwnProperty(propName)) {
                a[propName] = b[propName];
            }
L
lang 已提交
39 40
        });
    };
L
lang 已提交
41

L
lang 已提交
42
    /**
L
lang 已提交
43 44
     * @constructor
     * @alias module:echarts/data/List
L
lang 已提交
45 46
     *
     * @param {Array.<string>} dimensions
L
lang 已提交
47
     *        Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
L
lang 已提交
48
     * @param {module:echarts/model/Model} hostModel
L
lang 已提交
49
     */
L
lang 已提交
50
    var List = function (dimensions, hostModel) {
L
lang 已提交
51 52 53

        dimensions = dimensions || ['x', 'y'];

L
lang 已提交
54
        var dimensionInfos = {};
L
lang 已提交
55 56 57 58 59 60 61 62
        var dimensionNames = [];
        for (var i = 0; i < dimensions.length; i++) {
            var dimensionName;
            var dimensionInfo = {};
            if (typeof dimensions[i] === 'string') {
                dimensionName = dimensions[i];
                dimensionInfo = {
                    name: dimensionName,
L
lang 已提交
63
                    stackable: false,
L
lang 已提交
64
                    // Type can be 'float', 'int', 'number'
L
lang 已提交
65
                    // Default is number, Precision of float may not enough
L
lang 已提交
66 67
                    type: 'number'
                };
L
lang 已提交
68
            }
L
lang 已提交
69 70 71
            else {
                dimensionInfo = dimensions[i];
                dimensionName = dimensionInfo.name;
L
lang 已提交
72
                dimensionInfo.type = dimensionInfo.type || 'number';
L
lang 已提交
73 74
            }
            dimensionNames.push(dimensionName);
L
lang 已提交
75
            dimensionInfos[dimensionName] = dimensionInfo;
L
lang 已提交
76
        }
L
lang 已提交
77 78 79 80 81
        /**
         * @readOnly
         * @type {Array.<string>}
         */
        this.dimensions = dimensionNames;
P
pah100 已提交
82

L
lang 已提交
83 84
        /**
         * Infomation of each data dimension, like data type.
L
lang 已提交
85
         * @type {Object}
L
lang 已提交
86 87
         */
        this._dimensionInfos = dimensionInfos;
L
lang 已提交
88

L
lang 已提交
89 90 91
        /**
         * @type {module:echarts/model/Model}
         */
L
lang 已提交
92
        this.hostModel = hostModel;
L
lang 已提交
93

94
        /**
L
lang 已提交
95 96 97 98
         * Indices stores the indices of data subset after filtered.
         * This data subset will be used in chart.
         * @type {Array.<number>}
         * @readOnly
99
         */
L
lang 已提交
100
        this.indices = [];
101 102

        /**
L
lang 已提交
103 104 105
         * Data storage
         * @type {Object.<key, TypedArray|Array>}
         * @private
106
         */
L
lang 已提交
107
        this._storage = {};
108

L
lang 已提交
109 110 111 112
        /**
         * @type {Array.<string>}
         */
        this._nameList = [];
L
lang 已提交
113 114 115 116
        /**
         * @type {Array.<string>}
         */
        this._idList = [];
L
lang 已提交
117
        /**
L
lang 已提交
118 119 120
         * Models of data option is stored sparse for optimizing memory cost
         * @type {Array.<module:echarts/model/Model>}
         * @private
L
lang 已提交
121
         */
L
lang 已提交
122
        this._optionModels = [];
L
lang 已提交
123 124

        /**
L
lang 已提交
125
         * @param {module:echarts/data/List}
L
lang 已提交
126
         */
L
lang 已提交
127
        this.stackedOn = null;
L
lang 已提交
128

129
        /**
L
lang 已提交
130 131 132
         * Global visual properties after visual coding
         * @type {Object}
         * @private
133
         */
L
lang 已提交
134
        this._visual = {};
L
lang 已提交
135

D
deqingli 已提交
136 137 138 139 140 141 142
        /**
         * Globel layout properties.
         * @type {Object}
         * @private
         */
        this._layout = {};

L
lang 已提交
143
        /**
L
lang 已提交
144 145 146
         * Item visual properties after visual coding
         * @type {Array.<Object>}
         * @private
L
lang 已提交
147
         */
L
lang 已提交
148
        this._itemVisuals = [];
L
lang 已提交
149

L
lang 已提交
150 151 152 153 154 155 156 157 158 159 160 161 162
        /**
         * Item layout properties after layout
         * @type {Array.<Object>}
         * @private
         */
        this._itemLayouts = [];

        /**
         * Graphic elemnents
         * @type {Array.<module:zrender/Element>}
         * @private
         */
        this._graphicEls = [];
L
lang 已提交
163 164

        /**
L
lang 已提交
165
         * @type {Array.<Array|Object>}
L
lang 已提交
166
         * @private
L
lang 已提交
167
         */
L
lang 已提交
168
        this._rawData;
169 170 171 172 173 174

        /**
         * @type {Object}
         * @private
         */
        this._extent;
L
lang 已提交
175
    };
L
lang 已提交
176 177 178 179 180

    var listProto = List.prototype;

    listProto.type = 'list';

L
lang 已提交
181 182 183 184 185
    /**
     * Get dimension name
     * @param {string|number} 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'
P
pah100 已提交
186
     * @return {string} Concrete dim name.
L
lang 已提交
187 188 189 190 191 192 193
     */
    listProto.getDimension = function (dim) {
        if (!isNaN(dim)) {
            dim = this.dimensions[dim] || dim;
        }
        return dim;
    };
L
lang 已提交
194 195
    /**
     * Get type and stackable info of particular dimension
L
lang 已提交
196 197 198
     * @param {string|number} 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'
L
lang 已提交
199 200
     */
    listProto.getDimensionInfo = function (dim) {
201
        return zrUtil.clone(this._dimensionInfos[this.getDimension(dim)]);
L
lang 已提交
202 203
    };

L
lang 已提交
204 205
    /**
     * Initialize from data
L
lang 已提交
206 207
     * @param {Array.<Object|number|Array>} data
     * @param {Array.<string>} [nameList]
L
lang 已提交
208
     * @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number
L
lang 已提交
209
     */
L
lang 已提交
210
    listProto.initData = function (data, nameList, dimValueGetter) {
L
Comma  
lang 已提交
211
        data = data || [];
L
lang 已提交
212

L
lang 已提交
213 214 215 216
        if (!zrUtil.isArray(data)) {
            throw new Error('Invalid data.');
        }

L
lang 已提交
217 218
        this._rawData = data;

L
lang 已提交
219 220 221 222 223 224
        // Clear
        var storage = this._storage = {};
        var indices = this.indices = [];

        var dimensions = this.dimensions;
        var size = data.length;
L
Add pie  
lang 已提交
225
        var dimensionInfoMap = this._dimensionInfos;
L
lang 已提交
226

L
lang 已提交
227 228 229
        var idList = [];
        var nameRepeatCount = {};

L
lang 已提交
230 231
        nameList = nameList || [];

L
lang 已提交
232 233
        // Init storage
        for (var i = 0; i < dimensions.length; i++) {
L
lang 已提交
234
            var dimInfo = dimensionInfoMap[dimensions[i]];
L
lang 已提交
235 236 237 238
            var DataCtor = dataCtors[dimInfo.type];
            storage[dimensions[i]] = new DataCtor(size);
        }

L
lang 已提交
239 240
        // Default dim value getter
        dimValueGetter = dimValueGetter || function (dataItem, dimName, dataIndex, dimIndex) {
P
pah100 已提交
241 242 243 244 245 246 247 248
            var value = modelUtil.getDataItemValue(dataItem);
            return modelUtil.converDataValue(
                zrUtil.isArray(value)
                    ? value[dimIndex]
                    // If value is a single number or something else not array.
                    : value,
                dimensionInfoMap[dimName]
            );
L
lang 已提交
249
        };
L
lang 已提交
250

L
lang 已提交
251
        for (var idx = 0; idx < data.length; idx++) {
L
lang 已提交
252
            var dataItem = data[idx];
L
lang 已提交
253 254 255
            // Each data item is value
            // [1, 2]
            // 2
L
lang 已提交
256 257
            // Bar chart, line chart which uses category axis
            // only gives the 'y' value. 'x' value is the indices of cateogry
L
Add pie  
lang 已提交
258
            // Use a tempValue to normalize the value to be a (x, y) value
259

L
lang 已提交
260 261 262 263 264
            // Store the data by dimensions
            for (var k = 0; k < dimensions.length; k++) {
                var dim = dimensions[k];
                var dimStorage = storage[dim];
                // PENDING NULL is empty or zero
L
lang 已提交
265
                dimStorage[idx] = dimValueGetter(dataItem, dim, idx, k);
266 267
            }

L
lang 已提交
268 269
            indices.push(idx);
        }
L
lang 已提交
270

L
lang 已提交
271
        // Use the name in option and create id
L
lang 已提交
272
        for (var i = 0; i < data.length; i++) {
L
lang 已提交
273
            var id = '';
L
lang 已提交
274
            if (!nameList[i]) {
L
lang 已提交
275 276 277
                nameList[i] = data[i].name;
                // Try using the id in option
                id = data[i].id;
L
lang 已提交
278 279 280 281 282 283 284 285
            }
            var name = nameList[i] || '';
            if (!id && name) {
                // 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];
286
                }
L
lang 已提交
287
                nameRepeatCount[name]++;
288
            }
L
lang 已提交
289
            id && (idList[i] = id);
290 291
        }

L
lang 已提交
292
        this._nameList = nameList;
L
lang 已提交
293
        this._idList = idList;
L
lang 已提交
294 295 296 297 298 299 300 301 302 303
    };

    /**
     * @return {number}
     */
    listProto.count = function () {
        return this.indices.length;
    };

    /**
L
lang 已提交
304
     * Get value. Return NaN if idx is out of range.
L
lang 已提交
305
     * @param {string} dim Dim must be concrete name.
L
lang 已提交
306 307 308 309 310 311 312 313
     * @param {number} idx
     * @param {boolean} stack
     * @return {number}
     */
    listProto.get = function (dim, idx, stack) {
        var storage = this._storage;
        var dataIndex = this.indices[idx];

L
lang 已提交
314 315 316 317 318
        // If value not exists
        if (dataIndex == null) {
            return NaN;
        }

L
lang 已提交
319
        var value = storage[dim] && storage[dim][dataIndex];
L
lang 已提交
320
        // FIXME ordinal data type is not stackable
L
tweak  
lang 已提交
321 322 323 324 325 326 327 328 329 330 331 332 333 334
        if (stack) {
            var dimensionInfo = this._dimensionInfos[dim];
            if (dimensionInfo && dimensionInfo.stackable) {
                var stackedOn = this.stackedOn;
                while (stackedOn) {
                    // Get no stacked data of stacked on
                    var stackedValue = stackedOn.get(dim, idx);
                    // 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;
335
                }
L
lang 已提交
336 337 338 339
            }
        }
        return value;
    };
L
lang 已提交
340

P
pah100 已提交
341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
    /**
     * Get value for multi dimensions.
     * @param {Array.<string>} [dimensions] If ignored, using all dimensions.
     * @param {number} idx
     * @param {boolean} stack
     * @return {number}
     */
    listProto.getValues = function (dimensions, idx, stack) {
        var values = [];

        if (!zrUtil.isArray(dimensions)) {
            stack = idx;
            idx = dimensions;
            dimensions = this.dimensions;
        }

        for (var i = 0, len = dimensions.length; i < len; i++) {
            values.push(this.get(dimensions[i], idx, stack));
        }

        return values;
    };

L
lang 已提交
364 365 366 367 368 369 370 371
    /**
     * If value is NaN. Inlcuding '-'
     * @param {string} dim
     * @param {number} idx
     * @return {number}
     */
    listProto.hasValue = function (idx) {
        var dimensions = this.dimensions;
L
lang 已提交
372
        var dimensionInfos = this._dimensionInfos;
L
lang 已提交
373
        for (var i = 0, len = dimensions.length; i < len; i++) {
L
lang 已提交
374 375 376 377 378
            if (
                // Ordinal type can be string or number
                dimensionInfos[dimensions[i]].type !== 'ordinal'
                && isNaN(this.get(dimensions[i], idx))
            ) {
L
lang 已提交
379
                return false;
L
lang 已提交
380
            }
L
lang 已提交
381 382
        }
        return true;
L
lang 已提交
383
    };
L
lang 已提交
384

L
lang 已提交
385 386 387 388 389 390 391
    /**
     * Get extent of data in one dimension
     * @param {string} dim
     * @param {boolean} stack
     */
    listProto.getDataExtent = function (dim, stack) {
        var dimData = this._storage[dim];
L
tweak  
lang 已提交
392 393 394
        var dimInfo = this.getDimensionInfo(dim);
        stack = (dimInfo && dimInfo.stackable) && stack;
        var dimExtent = (this._extent || (this._extent = {}))[dim + (!!stack)];
L
lang 已提交
395
        var value;
L
lang 已提交
396 397 398
        if (dimExtent) {
            return dimExtent;
        }
L
lang 已提交
399
        // var dimInfo = this._dimensionInfos[dim];
L
lang 已提交
400
        if (dimData) {
L
lang 已提交
401 402
            var min = Infinity;
            var max = -Infinity;
L
lang 已提交
403 404
            // var isOrdinal = dimInfo.type === 'ordinal';
            for (var i = 0, len = this.count(); i < len; i++) {
L
lang 已提交
405
                value = this.get(dim, i, stack);
L
lang 已提交
406 407 408 409
                // FIXME
                // if (isOrdinal && typeof value === 'string') {
                //     value = zrUtil.indexOf(dimData, value);
                // }
L
lang 已提交
410 411 412
                value < min && (min = value);
                value > max && (max = value);
            }
L
tweak  
lang 已提交
413
            return (this._extent[dim + stack] = [min, max]);
L
lang 已提交
414 415 416
        }
        else {
            return [Infinity, -Infinity];
L
lang 已提交
417 418
        }
    };
L
lang 已提交
419

L
Add pie  
lang 已提交
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
    /**
     * Get sum of data in one dimension
     * @param {string} dim
     * @param {boolean} stack
     */
    listProto.getSum = function (dim, stack) {
        var dimData = this._storage[dim];
        var sum = 0;
        if (dimData) {
            for (var i = 0, len = this.count(); i < len; i++) {
                var value = this.get(dim, i, stack);
                if (!isNaN(value)) {
                    sum += value;
                }
            }
        }
        return sum;
    };
L
lang 已提交
438

L
lang 已提交
439 440 441 442 443 444 445 446 447 448
    /**
     * Retreive the index with given value
     * @param {number} idx
     * @param {number} value
     * @return {number}
     */
    // FIXME Precision of float value
    listProto.indexOf = function (dim, value) {
        var storage = this._storage;
        var dimData = storage[dim];
L
lang 已提交
449
        var indices = this.indices;
L
lang 已提交
450 451

        if (dimData) {
L
lang 已提交
452 453 454
            for (var i = 0, len = indices.length; i < len; i++) {
                var rawIndex = indices[i];
                if (dimData[rawIndex] === value) {
L
lang 已提交
455 456 457 458 459 460 461
                    return i;
                }
            }
        }
        return -1;
    };

L
lang 已提交
462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
    /**
     * Retreive the index with given name
     * @param {number} idx
     * @param {number} name
     * @return {number}
     */
    listProto.indexOfName = function (name) {
        var indices = this.indices;
        var nameList = this._nameList;

        for (var i = 0, len = indices.length; i < len; i++) {
            var rawIndex = indices[i];
            if (nameList[rawIndex] === name) {
                return i;
            }
        }

        return -1;
    };

L
lang 已提交
482 483
    /**
     * Retreive the index of nearest value
P
pah100 已提交
484
     * @param {string} dim
L
lang 已提交
485
     * @param {number} value
L
lang 已提交
486
     * @param {boolean} stack If given value is after stacked
L
lang 已提交
487 488
     * @return {number}
     */
L
lang 已提交
489
    listProto.indexOfNearest = function (dim, value, stack) {
L
lang 已提交
490 491 492 493 494 495
        var storage = this._storage;
        var dimData = storage[dim];

        if (dimData) {
            var minDist = Number.MAX_VALUE;
            var nearestIdx = -1;
496
            for (var i = 0, len = this.count(); i < len; i++) {
L
lang 已提交
497 498 499 500 501 502 503 504
                var diff = value - this.get(dim, i, stack);
                var dist = Math.abs(diff);
                if (dist < minDist
                    // For the case of two data are same on xAxis, which has sequence data.
                    // Show the nearest index
                    // https://github.com/ecomfe/echarts/issues/2869
                    || (dist === minDist && diff > 0)
                ) {
505 506
                    minDist = dist;
                    nearestIdx = i;
L
lang 已提交
507 508 509 510 511
                }
            }
            return nearestIdx;
        }
        return -1;
L
lang 已提交
512
    };
L
lang 已提交
513

L
lang 已提交
514 515
    /**
     * Get raw data index
L
lang 已提交
516 517
     * @param {number} idx
     * @return {number}
L
lang 已提交
518
     */
L
lang 已提交
519
    listProto.getRawIndex = function (idx) {
520 521
        var rawIdx = this.indices[idx];
        return rawIdx == null ? -1 : rawIdx;
L
lang 已提交
522
    };
L
lang 已提交
523

524 525 526 527 528 529 530 531 532
    /**
     * Get raw data item
     * @param {number} idx
     * @return {number}
     */
    listProto.getRawDataItem = function (idx) {
        return (this._rawData || [])[this.getRawIndex(idx)];
    };

L
lang 已提交
533 534
    /**
     * @param {number} idx
535
     * @param {boolean} [notDefaultIdx=false]
L
lang 已提交
536 537
     * @return {string}
     */
L
lang 已提交
538 539 540 541 542 543 544 545 546 547 548
    listProto.getName = function (idx) {
        return this._nameList[this.indices[idx]] || '';
    };

    /**
     * @param {number} idx
     * @param {boolean} [notDefaultIdx=false]
     * @return {string}
     */
    listProto.getId = function (idx) {
        return this._idList[this.indices[idx]] || (this.getRawIndex(idx) + '');
L
lang 已提交
549 550 551
    };


L
lang 已提交
552
    function normalizeDimensions(dimensions) {
553
        if (!zrUtil.isArray(dimensions)) {
L
lang 已提交
554 555 556 557
            dimensions = [dimensions];
        }
        return dimensions;
    }
L
lang 已提交
558

L
lang 已提交
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
    /**
     * Data iteration
     * @param {string|Array.<string>}
     * @param {Function} cb
     * @param {boolean} [stack=false]
     * @param {*} [context=this]
     *
     * @example
     *  list.each('x', function (x, idx) {});
     *  list.each(['x', 'y'], function (x, y, idx) {});
     *  list.each(function (idx) {})
     */
    listProto.each = function (dimensions, cb, stack, context) {
        if (typeof dimensions === 'function') {
            context = stack;
            stack = cb;
            cb = dimensions;
            dimensions = [];
        }
L
lang 已提交
578

L
lang 已提交
579 580 581
        dimensions = zrUtil.map(
            normalizeDimensions(dimensions), this.getDimension, this
        );
L
lang 已提交
582

L
lang 已提交
583 584 585
        var value = [];
        var dimSize = dimensions.length;
        var indices = this.indices;
L
lang 已提交
586

L
lang 已提交
587
        context = context || this;
L
lang 已提交
588

L
lang 已提交
589 590
        for (var i = 0; i < indices.length; i++) {
            if (dimSize === 0) {
L
lang 已提交
591
                cb.call(context, i);
L
lang 已提交
592 593 594
            }
            // Simple optimization
            else if (dimSize === 1) {
L
lang 已提交
595
                cb.call(context, this.get(dimensions[0], i, stack), i);
L
lang 已提交
596 597 598
            }
            else {
                for (var k = 0; k < dimSize; k++) {
L
lang 已提交
599
                    value[k] = this.get(dimensions[k], i, stack);
L
lang 已提交
600 601 602 603 604 605 606
                }
                // Index
                value[k] = i;
                cb.apply(context, value);
            }
        }
    };
L
lang 已提交
607

L
lang 已提交
608 609 610 611 612 613 614 615
    /**
     * Data filter
     * @param {string|Array.<string>}
     * @param {Function} cb
     * @param {boolean} [stack=false]
     * @param {*} [context=this]
     */
    listProto.filterSelf = function (dimensions, cb, stack, context) {
L
lang 已提交
616 617 618 619 620 621 622
        if (typeof dimensions === 'function') {
            context = stack;
            stack = cb;
            cb = dimensions;
            dimensions = [];
        }

L
lang 已提交
623 624 625
        dimensions = zrUtil.map(
            normalizeDimensions(dimensions), this.getDimension, this
        );
L
lang 已提交
626 627 628 629 630 631 632 633 634 635 636 637

        var newIndices = [];
        var value = [];
        var dimSize = dimensions.length;
        var indices = this.indices;

        context = context || this;

        for (var i = 0; i < indices.length; i++) {
            var keep;
            // Simple optimization
            if (dimSize === 1) {
L
lang 已提交
638
                keep = cb.call(
L
lang 已提交
639
                    context, this.get(dimensions[0], i, stack), i
L
lang 已提交
640 641 642 643
                );
            }
            else {
                for (var k = 0; k < dimSize; k++) {
L
lang 已提交
644
                    value[k] = this.get(dimensions[k], i, stack);
L
lang 已提交
645 646 647 648 649 650 651 652
                }
                value[k] = i;
                keep = cb.apply(context, value);
            }
            if (keep) {
                newIndices.push(indices[i]);
            }
        }
P
pah100 已提交
653

L
lang 已提交
654
        this.indices = newIndices;
L
lang 已提交
655

L
tweak  
lang 已提交
656 657 658
        // Reset data extent
        this._extent = {};

L
lang 已提交
659 660
        return this;
    };
L
lang 已提交
661

L
lang 已提交
662
    /**
L
lang 已提交
663 664
     * Data mapping to a plain array
     * @param {string|Array.<string>} [dimensions]
L
lang 已提交
665 666 667
     * @param {Function} cb
     * @param {boolean} [stack=false]
     * @param {*} [context=this]
L
lang 已提交
668
     * @return {Array}
L
lang 已提交
669
     */
L
lang 已提交
670
    listProto.mapArray = function (dimensions, cb, stack, context) {
L
lang 已提交
671 672 673 674 675 676
        if (typeof dimensions === 'function') {
            context = stack;
            stack = cb;
            cb = dimensions;
            dimensions = [];
        }
L
lang 已提交
677

L
lang 已提交
678 679 680 681 682 683 684
        var result = [];
        this.each(dimensions, function () {
            result.push(cb && cb.apply(this, arguments));
        }, stack, context);
        return result;
    };

L
lang 已提交
685
    function cloneListForMapAndSample(original, excludeDimensions) {
686
        var allDimensions = original.dimensions;
L
lang 已提交
687
        var list = new List(
688 689
            zrUtil.map(allDimensions, original.getDimensionInfo, original),
            original.hostModel
L
lang 已提交
690
        );
L
lang 已提交
691
        // FIXME If needs stackedOn, value may already been stacked
L
lang 已提交
692
        transferImmuProperties(list, original, original._wrappedMethods);
L
lang 已提交
693 694

        var storage = list._storage = {};
L
lang 已提交
695
        var originalStorage = original._storage;
L
lang 已提交
696
        // Init storage
L
lang 已提交
697 698
        for (var i = 0; i < allDimensions.length; i++) {
            var dim = allDimensions[i];
L
lang 已提交
699 700
            var dimStore = originalStorage[dim];
            if (zrUtil.indexOf(excludeDimensions, dim) >= 0) {
L
lang 已提交
701
                storage[dim] = new dimStore.constructor(
L
lang 已提交
702
                    originalStorage[dim].length
L
lang 已提交
703 704
                );
            }
L
lang 已提交
705
            else {
L
lang 已提交
706 707
                // Direct reference for other dimensions
                storage[dim] = originalStorage[dim];
L
lang 已提交
708
            }
L
lang 已提交
709
        }
L
lang 已提交
710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731
        return list;
    }

    /**
     * Data mapping to a new List with given dimensions
     * @param {string|Array.<string>} dimensions
     * @param {Function} cb
     * @param {boolean} [stack=false]
     * @param {*} [context=this]
     * @return {Array}
     */
    listProto.map = function (dimensions, cb, stack, context) {
        dimensions = zrUtil.map(
            normalizeDimensions(dimensions), this.getDimension, this
        );

        var list = cloneListForMapAndSample(this, dimensions);
        // Following properties are all immutable.
        // So we can reference to the same value
        var indices = list.indices = this.indices;

        var storage = list._storage;
L
lang 已提交
732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751

        var tmpRetValue = [];
        this.each(dimensions, function () {
            var idx = arguments[arguments.length - 1];
            var retValue = cb && cb.apply(this, arguments);
            if (retValue != null) {
                // a number
                if (typeof retValue === 'number') {
                    tmpRetValue[0] = retValue;
                    retValue = tmpRetValue;
                }
                for (var i = 0; i < retValue.length; i++) {
                    var dim = dimensions[i];
                    var dimStore = storage[dim];
                    var rawIdx = indices[idx];
                    if (dimStore) {
                        dimStore[rawIdx] = retValue[i];
                    }
                }
            }
P
tweak  
pah100 已提交
752
        }, stack, context);
L
lang 已提交
753 754 755 756

        return list;
    };

L
lang 已提交
757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
    /**
     * Large data down sampling on given dimension
     * @param {string} dimension
     * @param {number} rate
     * @param {Function} sampleValue
     * @param {Function} sampleIndex Sample index for name and id
     */
    listProto.downSample = function (dimension, rate, sampleValue, sampleIndex) {
        var list = cloneListForMapAndSample(this, [dimension]);
        var storage = this._storage;
        var targetStorage = list._storage;

        var originalIndices = this.indices;
        var indices = list.indices = [];

        var frameValues = [];
        var frameIndices = [];
        var frameSize = Math.floor(1 / rate);

        var dimStore = targetStorage[dimension];
        var len = this.count();
        // Copy data from original data
        for (var i = 0; i < storage[dimension].length; i++) {
            targetStorage[dimension][i] = storage[dimension][i];
        }
        for (var i = 0; i < len; i += frameSize) {
            // Last frame
            if (frameSize > len - i) {
                frameSize = len - i;
                frameValues.length = frameSize;
            }
            for (var k = 0; k < frameSize; k++) {
L
lang 已提交
789 790 791
                var idx = originalIndices[i + k];
                frameValues[k] = dimStore[idx];
                frameIndices[k] = idx;
L
lang 已提交
792 793 794 795 796 797 798 799 800 801
            }
            var value = sampleValue(frameValues);
            var idx = frameIndices[sampleIndex(frameValues, value) || 0];
            // Only write value on the filtered data
            dimStore[idx] = value;
            indices.push(idx);
        }
        return list;
    };

L
lang 已提交
802 803
    /**
     * Get model of one data item.
L
lang 已提交
804 805
     *
     * @param {number} idx
L
lang 已提交
806
     */
L
lang 已提交
807
    // FIXME Model proxy ?
808
    listProto.getItemModel = function (idx) {
L
lang 已提交
809
        var hostModel = this.hostModel;
L
lang 已提交
810
        idx = this.indices[idx];
P
fix ut  
pah100 已提交
811
        return new Model(this._rawData[idx], hostModel, hostModel && hostModel.ecModel);
L
lang 已提交
812 813 814 815
    };

    /**
     * Create a data differ
L
tweak  
lang 已提交
816
     * @param {module:echarts/data/List} otherList
L
lang 已提交
817 818
     * @return {module:echarts/data/DataDiffer}
     */
L
tweak  
lang 已提交
819
    listProto.diff = function (otherList) {
L
lang 已提交
820
        var idList = this._idList;
821
        var otherIdList = otherList && otherList._idList;
822
        return new DataDiffer(
L
tweak  
lang 已提交
823
            otherList ? otherList.indices : [], this.indices, function (idx) {
824 825
                return otherIdList[idx] || (idx + '');
            }, function (idx) {
L
lang 已提交
826
                return idList[idx] || (idx + '');
827 828
            }
        );
L
lang 已提交
829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854
    };
    /**
     * Get visual property.
     * @param {string} key
     */
    listProto.getVisual = function (key) {
        var visual = this._visual;
        return visual && visual[key];
    };

    /**
     * Set visual property
     * @param {string|Object} key
     * @param {*} [value]
     *
     * @example
     *  setVisual('color', color);
     *  setVisual({
     *      'color': color
     *  });
     */
    listProto.setVisual = function (key, val) {
        if (isObject(key)) {
            for (var name in key) {
                if (key.hasOwnProperty(name)) {
                    this.setVisual(name, key[name]);
L
lang 已提交
855 856
                }
            }
L
lang 已提交
857 858 859 860 861
            return;
        }
        this._visual = this._visual || {};
        this._visual[key] = val;
    };
L
lang 已提交
862

D
deqingli 已提交
863 864
    /**
     * Set layout property.
865
     * @param {string} key
866
     * @param {*} [val]
D
deqingli 已提交
867 868
     */
    listProto.setLayout = function (key, val) {
869 870 871 872 873 874 875 876
        if (isObject(key)) {
            for (var name in key) {
                if (key.hasOwnProperty(name)) {
                    this.setLayout(name, key[name]);
                }
            }
            return;
        }
D
deqingli 已提交
877 878 879 880 881 882 883 884
        this._layout[key] = val;
    };

    /**
     * Get layout property.
     * @param  {string} key.
     * @return {*}
     */
885
    listProto.getLayout = function (key) {
D
deqingli 已提交
886 887 888
        return this._layout[key];
    };

L
lang 已提交
889 890 891 892 893 894 895
    /**
     * Get layout of single data item
     * @param {number} idx
     */
    listProto.getItemLayout = function (idx) {
        return this._itemLayouts[idx];
    },
L
lang 已提交
896

L
lang 已提交
897 898 899 900
    /**
     * Set layout of single data item
     * @param {number} idx
     * @param {Object} layout
L
lang 已提交
901
     * @param {boolean=} [merge=false]
L
lang 已提交
902
     */
P
tree  
pah100 已提交
903 904 905 906
    listProto.setItemLayout = function (idx, layout, merge) {
        this._itemLayouts[idx] = merge
            ? zrUtil.extend(this._itemLayouts[idx] || {}, layout)
            : layout;
L
lang 已提交
907
    },
L
lang 已提交
908

L
lang 已提交
909 910 911 912
    /**
     * Get visual property of single data item
     * @param {number} idx
     * @param {string} key
L
lang 已提交
913
     * @param {boolean} ignoreParent
L
lang 已提交
914
     */
L
lang 已提交
915
    listProto.getItemVisual = function (idx, key, ignoreParent) {
L
lang 已提交
916 917
        var itemVisual = this._itemVisuals[idx];
        var val = itemVisual && itemVisual[key];
L
lang 已提交
918
        if (val == null && !ignoreParent) {
L
lang 已提交
919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944
            // Use global visual property
            return this.getVisual(key);
        }
        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
     *  });
     */
    listProto.setItemVisual = function (idx, key, value) {
        var itemVisual = this._itemVisuals[idx] || {};
        this._itemVisuals[idx] = itemVisual;

        if (isObject(key)) {
            for (var name in key) {
                if (key.hasOwnProperty(name)) {
P
pah100 已提交
945
                    itemVisual[name] = key[name];
L
lang 已提交
946
                }
L
lang 已提交
947
            }
L
lang 已提交
948
            return;
L
lang 已提交
949
        }
L
lang 已提交
950
        itemVisual[key] = value;
L
lang 已提交
951 952
    };

L
lang 已提交
953 954 955
    var setItemDataAndSeriesIndex = function (child) {
        child.seriesIndex = this.seriesIndex;
        child.dataIndex = this.dataIndex;
L
lang 已提交
956
    };
L
lang 已提交
957
    /**
958
     * Set graphic element relative to data. It can be set as null
L
lang 已提交
959
     * @param {number} idx
960
     * @param {module:zrender/Element} [el]
L
lang 已提交
961 962
     */
    listProto.setItemGraphicEl = function (idx, el) {
L
lang 已提交
963
        var hostModel = this.hostModel;
P
pah100 已提交
964

P
pah100 已提交
965 966 967 968 969 970 971 972
        if (el) {
            // Add data index and series index for indexing the data by element
            // Useful in tooltip
            el.dataIndex = idx;
            el.seriesIndex = hostModel && hostModel.seriesIndex;
            if (el.type === 'group') {
                el.traverse(setItemDataAndSeriesIndex, el);
            }
P
pah100 已提交
973
        }
974 975

        this._graphicEls[idx] = el;
P
pah100 已提交
976 977
    };

L
lang 已提交
978 979 980 981 982 983 984 985 986 987 988 989 990
    /**
     * @param {number} idx
     * @return {module:zrender/Element}
     */
    listProto.getItemGraphicEl = function (idx) {
        return this._graphicEls[idx];
    };

    /**
     * @param {Function} cb
     * @param {*} context
     */
    listProto.eachItemGraphicEl = function (cb, context) {
L
lang 已提交
991 992 993 994 995
        zrUtil.each(this._graphicEls, function (el, idx) {
            if (el) {
                cb && cb.call(context, el, idx);
            }
        });
L
lang 已提交
996 997 998 999 1000 1001 1002
    };

    /**
     * Shallow clone a new list except visual and layout properties, and graph elements.
     * New list only change the indices.
     */
    listProto.cloneShallow = function () {
L
lang 已提交
1003
        var dimensionInfoList = zrUtil.map(this.dimensions, this.getDimensionInfo, this);
L
lang 已提交
1004
        var list = new List(dimensionInfoList, this.hostModel);
L
lang 已提交
1005 1006 1007

        // FIXME
        list._storage = this._storage;
L
lang 已提交
1008

1009
        transferImmuProperties(list, this, this._wrappedMethods);
L
lang 已提交
1010 1011

        list.indices = this.indices.slice();
P
pah100 已提交
1012

L
lang 已提交
1013 1014
        return list;
    };
L
lang 已提交
1015

1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033
    /**
     * Wrap some method to add more feature
     * @param {string} methodName
     * @param {Function} injectFunction
     */
    listProto.wrapMethod = function (methodName, injectFunction) {
        var originalMethod = this[methodName];
        if (typeof originalMethod !== 'function') {
            return;
        }
        this._wrappedMethods = this._wrappedMethods || [];
        this._wrappedMethods.push(methodName);
        this[methodName] = function () {
            var res = originalMethod.apply(this, arguments);
            return injectFunction.call(this, res);
        };
    };

L
lang 已提交
1034 1035
    return List;
});