List.js 29.5 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
        this._rawData = data;

L
lang 已提交
214 215 216 217 218 219
        // Clear
        var storage = this._storage = {};
        var indices = this.indices = [];

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

L
lang 已提交
222 223 224
        var idList = [];
        var nameRepeatCount = {};

L
lang 已提交
225 226
        nameList = nameList || [];

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

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

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

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

L
lang 已提交
263 264
            indices.push(idx);
        }
L
lang 已提交
265

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

L
lang 已提交
287
        this._nameList = nameList;
L
lang 已提交
288
        this._idList = idList;
L
lang 已提交
289 290 291 292 293 294 295 296 297 298
    };

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

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

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

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

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

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

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

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

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

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

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

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


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

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

L
lang 已提交
566 567 568
        dimensions = zrUtil.map(
            normalizeDimensions(dimensions), this.getDimension, this
        );
L
lang 已提交
569

L
lang 已提交
570 571 572
        var value = [];
        var dimSize = dimensions.length;
        var indices = this.indices;
L
lang 已提交
573

L
lang 已提交
574
        context = context || this;
L
lang 已提交
575

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

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

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

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

L
lang 已提交
641
        this.indices = newIndices;
L
lang 已提交
642

L
tweak  
lang 已提交
643 644 645
        // Reset data extent
        this._extent = {};

L
lang 已提交
646 647
        return this;
    };
L
lang 已提交
648

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

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

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

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

        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 已提交
739
        }, stack, context);
L
lang 已提交
740 741 742 743

        return list;
    };

L
lang 已提交
744 745 746 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
    /**
     * 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 已提交
776 777 778
                var idx = originalIndices[i + k];
                frameValues[k] = dimStore[idx];
                frameIndices[k] = idx;
L
lang 已提交
779 780 781 782 783 784 785 786 787 788
            }
            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 已提交
789 790
    /**
     * Get model of one data item.
L
lang 已提交
791 792
     *
     * @param {number} idx
L
lang 已提交
793
     */
L
lang 已提交
794
    // FIXME Model proxy ?
795
    listProto.getItemModel = function (idx) {
L
lang 已提交
796
        var hostModel = this.hostModel;
L
lang 已提交
797
        idx = this.indices[idx];
798
        return new Model(this._rawData[idx], hostModel, hostModel.ecModel);
L
lang 已提交
799 800 801 802
    };

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

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

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

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

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

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

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

P
pah100 已提交
952 953 954 955 956 957 958 959
        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 已提交
960
        }
961 962

        this._graphicEls[idx] = el;
P
pah100 已提交
963 964
    };

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

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

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

996
        transferImmuProperties(list, this, this._wrappedMethods);
L
lang 已提交
997 998

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

L
lang 已提交
1000 1001
        return list;
    };
L
lang 已提交
1002

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