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

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

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

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

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

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

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

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

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

L
lang 已提交
238 239
        // Default dim value getter
        dimValueGetter = dimValueGetter || function (dataItem, dimName, dataIndex, dimIndex) {
P
pah100 已提交
240 241 242 243 244 245 246 247
            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 已提交
248
        };
L
lang 已提交
249

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

L
lang 已提交
259 260 261 262 263
            // 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 已提交
264
                dimStorage[idx] = dimValueGetter(dataItem, dim, idx, k);
265 266
            }

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

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

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

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

    /**
L
lang 已提交
303
     * Get value. Return NaN if idx is out of range.
L
lang 已提交
304
     * @param {string} dim Dim must be concrete name.
L
lang 已提交
305 306 307 308 309 310 311 312
     * @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 已提交
313 314 315 316 317
        // If value not exists
        if (dataIndex == null) {
            return NaN;
        }

L
lang 已提交
318
        var value = storage[dim] && storage[dim][dataIndex];
L
lang 已提交
319
        // FIXME ordinal data type is not stackable
L
tweak  
lang 已提交
320 321 322 323 324 325 326 327 328 329 330 331 332 333
        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;
334
                }
L
lang 已提交
335 336 337 338
            }
        }
        return value;
    };
L
lang 已提交
339

P
pah100 已提交
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
    /**
     * 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 已提交
363 364 365 366 367 368 369 370
    /**
     * If value is NaN. Inlcuding '-'
     * @param {string} dim
     * @param {number} idx
     * @return {number}
     */
    listProto.hasValue = function (idx) {
        var dimensions = this.dimensions;
L
lang 已提交
371
        var dimensionInfos = this._dimensionInfos;
L
lang 已提交
372
        for (var i = 0, len = dimensions.length; i < len; i++) {
L
lang 已提交
373 374 375 376 377
            if (
                // Ordinal type can be string or number
                dimensionInfos[dimensions[i]].type !== 'ordinal'
                && isNaN(this.get(dimensions[i], idx))
            ) {
L
lang 已提交
378
                return false;
L
lang 已提交
379
            }
L
lang 已提交
380 381
        }
        return true;
L
lang 已提交
382
    };
L
lang 已提交
383

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

L
Add pie  
lang 已提交
419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
    /**
     * 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 已提交
437

L
lang 已提交
438 439 440 441 442 443 444 445 446 447
    /**
     * 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 已提交
448
        var indices = this.indices;
L
lang 已提交
449 450

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

L
lang 已提交
461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
    /**
     * 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 已提交
481 482
    /**
     * Retreive the index of nearest value
483
     * @param {string>} dim
L
lang 已提交
484
     * @param {number} value
L
lang 已提交
485
     * @param {boolean} stack If given value is after stacked
L
lang 已提交
486 487
     * @return {number}
     */
L
lang 已提交
488
    listProto.indexOfNearest = function (dim, value, stack) {
L
lang 已提交
489 490 491 492 493 494
        var storage = this._storage;
        var dimData = storage[dim];

        if (dimData) {
            var minDist = Number.MAX_VALUE;
            var nearestIdx = -1;
495
            for (var i = 0, len = this.count(); i < len; i++) {
L
lang 已提交
496 497 498 499 500 501 502 503
                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)
                ) {
504 505
                    minDist = dist;
                    nearestIdx = i;
L
lang 已提交
506 507 508 509 510
                }
            }
            return nearestIdx;
        }
        return -1;
L
lang 已提交
511
    };
L
lang 已提交
512

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

L
lang 已提交
523 524
    /**
     * @param {number} idx
525
     * @param {boolean} [notDefaultIdx=false]
L
lang 已提交
526 527
     * @return {string}
     */
L
lang 已提交
528 529 530 531 532 533 534 535 536 537 538
    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 已提交
539 540 541
    };


L
lang 已提交
542
    function normalizeDimensions(dimensions) {
543
        if (!zrUtil.isArray(dimensions)) {
L
lang 已提交
544 545 546 547
            dimensions = [dimensions];
        }
        return dimensions;
    }
L
lang 已提交
548

L
lang 已提交
549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567
    /**
     * 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 已提交
568

L
lang 已提交
569 570 571
        dimensions = zrUtil.map(
            normalizeDimensions(dimensions), this.getDimension, this
        );
L
lang 已提交
572

L
lang 已提交
573 574 575
        var value = [];
        var dimSize = dimensions.length;
        var indices = this.indices;
L
lang 已提交
576

L
lang 已提交
577
        context = context || this;
L
lang 已提交
578

L
lang 已提交
579 580
        for (var i = 0; i < indices.length; i++) {
            if (dimSize === 0) {
L
lang 已提交
581
                cb.call(context, i);
L
lang 已提交
582 583 584
            }
            // Simple optimization
            else if (dimSize === 1) {
L
lang 已提交
585
                cb.call(context, this.get(dimensions[0], i, stack), i);
L
lang 已提交
586 587 588
            }
            else {
                for (var k = 0; k < dimSize; k++) {
L
lang 已提交
589
                    value[k] = this.get(dimensions[k], i, stack);
L
lang 已提交
590 591 592 593 594 595 596
                }
                // Index
                value[k] = i;
                cb.apply(context, value);
            }
        }
    };
L
lang 已提交
597

L
lang 已提交
598 599 600 601 602 603 604 605
    /**
     * 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 已提交
606 607 608 609 610 611 612
        if (typeof dimensions === 'function') {
            context = stack;
            stack = cb;
            cb = dimensions;
            dimensions = [];
        }

L
lang 已提交
613 614 615
        dimensions = zrUtil.map(
            normalizeDimensions(dimensions), this.getDimension, this
        );
L
lang 已提交
616 617 618 619 620 621 622 623 624 625 626 627

        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 已提交
628
                keep = cb.call(
L
lang 已提交
629
                    context, this.get(dimensions[0], i, stack), i
L
lang 已提交
630 631 632 633
                );
            }
            else {
                for (var k = 0; k < dimSize; k++) {
L
lang 已提交
634
                    value[k] = this.get(dimensions[k], i, stack);
L
lang 已提交
635 636 637 638 639 640 641 642
                }
                value[k] = i;
                keep = cb.apply(context, value);
            }
            if (keep) {
                newIndices.push(indices[i]);
            }
        }
P
pah100 已提交
643

L
lang 已提交
644
        this.indices = newIndices;
L
lang 已提交
645

L
tweak  
lang 已提交
646 647 648
        // Reset data extent
        this._extent = {};

L
lang 已提交
649 650
        return this;
    };
L
lang 已提交
651

L
lang 已提交
652
    /**
L
lang 已提交
653 654
     * Data mapping to a plain array
     * @param {string|Array.<string>} [dimensions]
L
lang 已提交
655 656 657
     * @param {Function} cb
     * @param {boolean} [stack=false]
     * @param {*} [context=this]
L
lang 已提交
658
     * @return {Array}
L
lang 已提交
659
     */
L
lang 已提交
660
    listProto.mapArray = function (dimensions, cb, stack, context) {
L
lang 已提交
661 662 663 664 665 666
        if (typeof dimensions === 'function') {
            context = stack;
            stack = cb;
            cb = dimensions;
            dimensions = [];
        }
L
lang 已提交
667

L
lang 已提交
668 669 670 671 672 673 674
        var result = [];
        this.each(dimensions, function () {
            result.push(cb && cb.apply(this, arguments));
        }, stack, context);
        return result;
    };

L
lang 已提交
675
    function cloneListForMapAndSample(original, excludeDimensions) {
676
        var allDimensions = original.dimensions;
L
lang 已提交
677
        var list = new List(
678 679
            zrUtil.map(allDimensions, original.getDimensionInfo, original),
            original.hostModel
L
lang 已提交
680
        );
L
lang 已提交
681
        // FIXME If needs stackedOn, value may already been stacked
L
lang 已提交
682
        transferImmuProperties(list, original, original._wrappedMethods);
L
lang 已提交
683 684

        var storage = list._storage = {};
L
lang 已提交
685
        var originalStorage = original._storage;
L
lang 已提交
686
        // Init storage
L
lang 已提交
687 688
        for (var i = 0; i < allDimensions.length; i++) {
            var dim = allDimensions[i];
L
lang 已提交
689 690
            var dimStore = originalStorage[dim];
            if (zrUtil.indexOf(excludeDimensions, dim) >= 0) {
L
lang 已提交
691
                storage[dim] = new dimStore.constructor(
L
lang 已提交
692
                    originalStorage[dim].length
L
lang 已提交
693 694
                );
            }
L
lang 已提交
695
            else {
L
lang 已提交
696 697
                // Direct reference for other dimensions
                storage[dim] = originalStorage[dim];
L
lang 已提交
698
            }
L
lang 已提交
699
        }
L
lang 已提交
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
        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 已提交
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741

        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 已提交
742
        }, stack, context);
L
lang 已提交
743 744 745 746

        return list;
    };

L
lang 已提交
747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
    /**
     * 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 已提交
779 780 781
                var idx = originalIndices[i + k];
                frameValues[k] = dimStore[idx];
                frameIndices[k] = idx;
L
lang 已提交
782 783 784 785 786 787 788 789 790 791
            }
            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 已提交
792 793
    /**
     * Get model of one data item.
L
lang 已提交
794 795
     *
     * @param {number} idx
L
lang 已提交
796
     */
L
lang 已提交
797
    // FIXME Model proxy ?
798
    listProto.getItemModel = function (idx) {
L
lang 已提交
799
        var hostModel = this.hostModel;
L
lang 已提交
800
        idx = this.indices[idx];
P
fix ut  
pah100 已提交
801
        return new Model(this._rawData[idx], hostModel, hostModel && hostModel.ecModel);
L
lang 已提交
802 803 804 805
    };

    /**
     * Create a data differ
L
tweak  
lang 已提交
806
     * @param {module:echarts/data/List} otherList
L
lang 已提交
807 808
     * @return {module:echarts/data/DataDiffer}
     */
L
tweak  
lang 已提交
809
    listProto.diff = function (otherList) {
L
lang 已提交
810
        var idList = this._idList;
811
        var otherIdList = otherList && otherList._idList;
812
        return new DataDiffer(
L
tweak  
lang 已提交
813
            otherList ? otherList.indices : [], this.indices, function (idx) {
814 815
                return otherIdList[idx] || (idx + '');
            }, function (idx) {
L
lang 已提交
816
                return idList[idx] || (idx + '');
817 818
            }
        );
L
lang 已提交
819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844
    };
    /**
     * 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 已提交
845 846
                }
            }
L
lang 已提交
847 848 849 850 851
            return;
        }
        this._visual = this._visual || {};
        this._visual[key] = val;
    };
L
lang 已提交
852

D
deqingli 已提交
853 854
    /**
     * Set layout property.
855
     * @param {string} key
856
     * @param {*} [val]
D
deqingli 已提交
857 858
     */
    listProto.setLayout = function (key, val) {
859 860 861 862 863 864 865 866
        if (isObject(key)) {
            for (var name in key) {
                if (key.hasOwnProperty(name)) {
                    this.setLayout(name, key[name]);
                }
            }
            return;
        }
D
deqingli 已提交
867 868 869 870 871 872 873 874
        this._layout[key] = val;
    };

    /**
     * Get layout property.
     * @param  {string} key.
     * @return {*}
     */
875
    listProto.getLayout = function (key) {
D
deqingli 已提交
876 877 878
        return this._layout[key];
    };

L
lang 已提交
879 880 881 882 883 884 885
    /**
     * Get layout of single data item
     * @param {number} idx
     */
    listProto.getItemLayout = function (idx) {
        return this._itemLayouts[idx];
    },
L
lang 已提交
886

L
lang 已提交
887 888 889 890
    /**
     * Set layout of single data item
     * @param {number} idx
     * @param {Object} layout
L
lang 已提交
891
     * @param {boolean=} [merge=false]
L
lang 已提交
892
     */
P
tree  
pah100 已提交
893 894 895 896
    listProto.setItemLayout = function (idx, layout, merge) {
        this._itemLayouts[idx] = merge
            ? zrUtil.extend(this._itemLayouts[idx] || {}, layout)
            : layout;
L
lang 已提交
897
    },
L
lang 已提交
898

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

L
lang 已提交
943 944 945
    var setItemDataAndSeriesIndex = function (child) {
        child.seriesIndex = this.seriesIndex;
        child.dataIndex = this.dataIndex;
L
lang 已提交
946
    };
L
lang 已提交
947
    /**
948
     * Set graphic element relative to data. It can be set as null
L
lang 已提交
949
     * @param {number} idx
950
     * @param {module:zrender/Element} [el]
L
lang 已提交
951 952
     */
    listProto.setItemGraphicEl = function (idx, el) {
L
lang 已提交
953
        var hostModel = this.hostModel;
P
pah100 已提交
954

P
pah100 已提交
955 956 957 958 959 960 961 962
        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 已提交
963
        }
964 965

        this._graphicEls[idx] = el;
P
pah100 已提交
966 967
    };

L
lang 已提交
968 969 970 971 972 973 974 975 976 977 978 979 980
    /**
     * @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 已提交
981 982 983 984 985
        zrUtil.each(this._graphicEls, function (el, idx) {
            if (el) {
                cb && cb.call(context, el, idx);
            }
        });
L
lang 已提交
986 987 988 989 990 991 992
    };

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

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

999
        transferImmuProperties(list, this, this._wrappedMethods);
L
lang 已提交
1000 1001

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

L
lang 已提交
1003 1004
        return list;
    };
L
lang 已提交
1005

1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
    /**
     * 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 已提交
1024 1025
    return List;
});