List.js 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216
  1. var _config = require("../config");
  2. var __DEV__ = _config.__DEV__;
  3. var zrUtil = require("zrender/lib/core/util");
  4. var Model = require("../model/Model");
  5. var DataDiffer = require("./DataDiffer");
  6. var modelUtil = require("../util/model");
  7. /**
  8. * List for data storage
  9. * @module echarts/data/List
  10. */
  11. var isObject = zrUtil.isObject;
  12. var UNDEFINED = 'undefined';
  13. var globalObj = typeof window === UNDEFINED ? global : window;
  14. var dataCtors = {
  15. 'float': typeof globalObj.Float64Array === UNDEFINED ? Array : globalObj.Float64Array,
  16. 'int': typeof globalObj.Int32Array === UNDEFINED ? Array : globalObj.Int32Array,
  17. // Ordinal data type can be string or int
  18. 'ordinal': Array,
  19. 'number': Array,
  20. 'time': Array
  21. };
  22. var TRANSFERABLE_PROPERTIES = ['stackedOn', 'hasItemOption', '_nameList', '_idList', '_rawData'];
  23. function transferProperties(a, b) {
  24. zrUtil.each(TRANSFERABLE_PROPERTIES.concat(b.__wrappedMethods || []), function (propName) {
  25. if (b.hasOwnProperty(propName)) {
  26. a[propName] = b[propName];
  27. }
  28. });
  29. a.__wrappedMethods = b.__wrappedMethods;
  30. }
  31. function DefaultDataProvider(dataArray) {
  32. this._array = dataArray || [];
  33. }
  34. DefaultDataProvider.prototype.pure = false;
  35. DefaultDataProvider.prototype.count = function () {
  36. return this._array.length;
  37. };
  38. DefaultDataProvider.prototype.getItem = function (idx) {
  39. return this._array[idx];
  40. };
  41. /**
  42. * @constructor
  43. * @alias module:echarts/data/List
  44. *
  45. * @param {Array.<string|Object>} dimensions
  46. * For example, ['someDimName', {name: 'someDimName', type: 'someDimType'}, ...].
  47. * Dimensions should be concrete names like x, y, z, lng, lat, angle, radius
  48. * @param {module:echarts/model/Model} hostModel
  49. */
  50. var List = function (dimensions, hostModel) {
  51. dimensions = dimensions || ['x', 'y'];
  52. var dimensionInfos = {};
  53. var dimensionNames = [];
  54. for (var i = 0; i < dimensions.length; i++) {
  55. var dimensionName;
  56. var dimensionInfo = {};
  57. if (typeof dimensions[i] === 'string') {
  58. dimensionName = dimensions[i];
  59. dimensionInfo = {
  60. name: dimensionName,
  61. coordDim: dimensionName,
  62. coordDimIndex: 0,
  63. stackable: false,
  64. // Type can be 'float', 'int', 'number'
  65. // Default is number, Precision of float may not enough
  66. type: 'number'
  67. };
  68. } else {
  69. dimensionInfo = dimensions[i];
  70. dimensionName = dimensionInfo.name;
  71. dimensionInfo.type = dimensionInfo.type || 'number';
  72. if (!dimensionInfo.coordDim) {
  73. dimensionInfo.coordDim = dimensionName;
  74. dimensionInfo.coordDimIndex = 0;
  75. }
  76. }
  77. dimensionInfo.otherDims = dimensionInfo.otherDims || {};
  78. dimensionNames.push(dimensionName);
  79. dimensionInfos[dimensionName] = dimensionInfo;
  80. }
  81. /**
  82. * @readOnly
  83. * @type {Array.<string>}
  84. */
  85. this.dimensions = dimensionNames;
  86. /**
  87. * Infomation of each data dimension, like data type.
  88. * @type {Object}
  89. */
  90. this._dimensionInfos = dimensionInfos;
  91. /**
  92. * @type {module:echarts/model/Model}
  93. */
  94. this.hostModel = hostModel;
  95. /**
  96. * @type {module:echarts/model/Model}
  97. */
  98. this.dataType;
  99. /**
  100. * Indices stores the indices of data subset after filtered.
  101. * This data subset will be used in chart.
  102. * @type {Array.<number>}
  103. * @readOnly
  104. */
  105. this.indices = [];
  106. /**
  107. * Data storage
  108. * @type {Object.<key, TypedArray|Array>}
  109. * @private
  110. */
  111. this._storage = {};
  112. /**
  113. * @type {Array.<string>}
  114. */
  115. this._nameList = [];
  116. /**
  117. * @type {Array.<string>}
  118. */
  119. this._idList = [];
  120. /**
  121. * Models of data option is stored sparse for optimizing memory cost
  122. * @type {Array.<module:echarts/model/Model>}
  123. * @private
  124. */
  125. this._optionModels = [];
  126. /**
  127. * @param {module:echarts/data/List}
  128. */
  129. this.stackedOn = null;
  130. /**
  131. * Global visual properties after visual coding
  132. * @type {Object}
  133. * @private
  134. */
  135. this._visual = {};
  136. /**
  137. * Globel layout properties.
  138. * @type {Object}
  139. * @private
  140. */
  141. this._layout = {};
  142. /**
  143. * Item visual properties after visual coding
  144. * @type {Array.<Object>}
  145. * @private
  146. */
  147. this._itemVisuals = [];
  148. /**
  149. * Item layout properties after layout
  150. * @type {Array.<Object>}
  151. * @private
  152. */
  153. this._itemLayouts = [];
  154. /**
  155. * Graphic elemnents
  156. * @type {Array.<module:zrender/Element>}
  157. * @private
  158. */
  159. this._graphicEls = [];
  160. /**
  161. * @type {Array.<Array|Object>}
  162. * @private
  163. */
  164. this._rawData;
  165. /**
  166. * @type {Object}
  167. * @private
  168. */
  169. this._extent;
  170. };
  171. var listProto = List.prototype;
  172. listProto.type = 'list';
  173. /**
  174. * If each data item has it's own option
  175. * @type {boolean}
  176. */
  177. listProto.hasItemOption = true;
  178. /**
  179. * Get dimension name
  180. * @param {string|number} dim
  181. * Dimension can be concrete names like x, y, z, lng, lat, angle, radius
  182. * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
  183. * @return {string} Concrete dim name.
  184. */
  185. listProto.getDimension = function (dim) {
  186. if (!isNaN(dim)) {
  187. dim = this.dimensions[dim] || dim;
  188. }
  189. return dim;
  190. };
  191. /**
  192. * Get type and stackable info of particular dimension
  193. * @param {string|number} dim
  194. * Dimension can be concrete names like x, y, z, lng, lat, angle, radius
  195. * Or a ordinal number. For example getDimensionInfo(0) will return 'x' or 'lng' or 'radius'
  196. */
  197. listProto.getDimensionInfo = function (dim) {
  198. return zrUtil.clone(this._dimensionInfos[this.getDimension(dim)]);
  199. };
  200. /**
  201. * Initialize from data
  202. * @param {Array.<Object|number|Array>} data
  203. * @param {Array.<string>} [nameList]
  204. * @param {Function} [dimValueGetter] (dataItem, dimName, dataIndex, dimIndex) => number
  205. */
  206. listProto.initData = function (data, nameList, dimValueGetter) {
  207. data = data || [];
  208. var isDataArray = zrUtil.isArray(data);
  209. if (isDataArray) {
  210. data = new DefaultDataProvider(data);
  211. }
  212. this._rawData = data; // Clear
  213. var storage = this._storage = {};
  214. var indices = this.indices = [];
  215. var dimensions = this.dimensions;
  216. var dimensionInfoMap = this._dimensionInfos;
  217. var size = data.count();
  218. var idList = [];
  219. var nameRepeatCount = {};
  220. var nameDimIdx;
  221. nameList = nameList || []; // Init storage
  222. for (var i = 0; i < dimensions.length; i++) {
  223. var dimInfo = dimensionInfoMap[dimensions[i]];
  224. dimInfo.otherDims.itemName === 0 && (nameDimIdx = i);
  225. var DataCtor = dataCtors[dimInfo.type];
  226. storage[dimensions[i]] = new DataCtor(size);
  227. }
  228. var self = this;
  229. if (!dimValueGetter) {
  230. self.hasItemOption = false;
  231. } // Default dim value getter
  232. dimValueGetter = dimValueGetter || function (dataItem, dimName, dataIndex, dimIndex) {
  233. var value = modelUtil.getDataItemValue(dataItem); // If any dataItem is like { value: 10 }
  234. if (modelUtil.isDataItemOption(dataItem)) {
  235. self.hasItemOption = true;
  236. }
  237. return modelUtil.converDataValue(value instanceof Array ? value[dimIndex] // If value is a single number or something else not array.
  238. : value, dimensionInfoMap[dimName]);
  239. };
  240. for (var i = 0; i < size; i++) {
  241. // NOTICE: Try not to write things into dataItem
  242. var dataItem = data.getItem(i); // Each data item is value
  243. // [1, 2]
  244. // 2
  245. // Bar chart, line chart which uses category axis
  246. // only gives the 'y' value. 'x' value is the indices of cateogry
  247. // Use a tempValue to normalize the value to be a (x, y) value
  248. // Store the data by dimensions
  249. for (var k = 0; k < dimensions.length; k++) {
  250. var dim = dimensions[k];
  251. var dimStorage = storage[dim]; // PENDING NULL is empty or zero
  252. dimStorage[i] = dimValueGetter(dataItem, dim, i, k);
  253. }
  254. indices.push(i);
  255. } // Use the name in option and create id
  256. for (var i = 0; i < size; i++) {
  257. var dataItem = data.getItem(i);
  258. if (!nameList[i] && dataItem) {
  259. if (dataItem.name != null) {
  260. nameList[i] = dataItem.name;
  261. } else if (nameDimIdx != null) {
  262. nameList[i] = storage[dimensions[nameDimIdx]][i];
  263. }
  264. }
  265. var name = nameList[i] || ''; // Try using the id in option
  266. var id = dataItem && dataItem.id;
  267. if (!id && name) {
  268. // Use name as id and add counter to avoid same name
  269. nameRepeatCount[name] = nameRepeatCount[name] || 0;
  270. id = name;
  271. if (nameRepeatCount[name] > 0) {
  272. id += '__ec__' + nameRepeatCount[name];
  273. }
  274. nameRepeatCount[name]++;
  275. }
  276. id && (idList[i] = id);
  277. }
  278. this._nameList = nameList;
  279. this._idList = idList;
  280. };
  281. /**
  282. * @return {number}
  283. */
  284. listProto.count = function () {
  285. return this.indices.length;
  286. };
  287. /**
  288. * Get value. Return NaN if idx is out of range.
  289. * @param {string} dim Dim must be concrete name.
  290. * @param {number} idx
  291. * @param {boolean} stack
  292. * @return {number}
  293. */
  294. listProto.get = function (dim, idx, stack) {
  295. var storage = this._storage;
  296. var dataIndex = this.indices[idx]; // If value not exists
  297. if (dataIndex == null || !storage[dim]) {
  298. return NaN;
  299. }
  300. var value = storage[dim][dataIndex]; // FIXME ordinal data type is not stackable
  301. if (stack) {
  302. var dimensionInfo = this._dimensionInfos[dim];
  303. if (dimensionInfo && dimensionInfo.stackable) {
  304. var stackedOn = this.stackedOn;
  305. while (stackedOn) {
  306. // Get no stacked data of stacked on
  307. var stackedValue = stackedOn.get(dim, idx); // Considering positive stack, negative stack and empty data
  308. if (value >= 0 && stackedValue > 0 || // Positive stack
  309. value <= 0 && stackedValue < 0 // Negative stack
  310. ) {
  311. value += stackedValue;
  312. }
  313. stackedOn = stackedOn.stackedOn;
  314. }
  315. }
  316. }
  317. return value;
  318. };
  319. /**
  320. * Get value for multi dimensions.
  321. * @param {Array.<string>} [dimensions] If ignored, using all dimensions.
  322. * @param {number} idx
  323. * @param {boolean} stack
  324. * @return {number}
  325. */
  326. listProto.getValues = function (dimensions, idx, stack) {
  327. var values = [];
  328. if (!zrUtil.isArray(dimensions)) {
  329. stack = idx;
  330. idx = dimensions;
  331. dimensions = this.dimensions;
  332. }
  333. for (var i = 0, len = dimensions.length; i < len; i++) {
  334. values.push(this.get(dimensions[i], idx, stack));
  335. }
  336. return values;
  337. };
  338. /**
  339. * If value is NaN. Inlcuding '-'
  340. * @param {string} dim
  341. * @param {number} idx
  342. * @return {number}
  343. */
  344. listProto.hasValue = function (idx) {
  345. var dimensions = this.dimensions;
  346. var dimensionInfos = this._dimensionInfos;
  347. for (var i = 0, len = dimensions.length; i < len; i++) {
  348. if ( // Ordinal type can be string or number
  349. dimensionInfos[dimensions[i]].type !== 'ordinal' && isNaN(this.get(dimensions[i], idx))) {
  350. return false;
  351. }
  352. }
  353. return true;
  354. };
  355. /**
  356. * Get extent of data in one dimension
  357. * @param {string} dim
  358. * @param {boolean} stack
  359. * @param {Function} filter
  360. */
  361. listProto.getDataExtent = function (dim, stack, filter) {
  362. dim = this.getDimension(dim);
  363. var dimData = this._storage[dim];
  364. var dimInfo = this.getDimensionInfo(dim);
  365. stack = dimInfo && dimInfo.stackable && stack;
  366. var dimExtent = (this._extent || (this._extent = {}))[dim + !!stack];
  367. var value;
  368. if (dimExtent) {
  369. return dimExtent;
  370. } // var dimInfo = this._dimensionInfos[dim];
  371. if (dimData) {
  372. var min = Infinity;
  373. var max = -Infinity; // var isOrdinal = dimInfo.type === 'ordinal';
  374. for (var i = 0, len = this.count(); i < len; i++) {
  375. value = this.get(dim, i, stack); // FIXME
  376. // if (isOrdinal && typeof value === 'string') {
  377. // value = zrUtil.indexOf(dimData, value);
  378. // }
  379. if (!filter || filter(value, dim, i)) {
  380. value < min && (min = value);
  381. value > max && (max = value);
  382. }
  383. }
  384. return this._extent[dim + !!stack] = [min, max];
  385. } else {
  386. return [Infinity, -Infinity];
  387. }
  388. };
  389. /**
  390. * Get sum of data in one dimension
  391. * @param {string} dim
  392. * @param {boolean} stack
  393. */
  394. listProto.getSum = function (dim, stack) {
  395. var dimData = this._storage[dim];
  396. var sum = 0;
  397. if (dimData) {
  398. for (var i = 0, len = this.count(); i < len; i++) {
  399. var value = this.get(dim, i, stack);
  400. if (!isNaN(value)) {
  401. sum += value;
  402. }
  403. }
  404. }
  405. return sum;
  406. };
  407. /**
  408. * Retreive the index with given value
  409. * @param {number} idx
  410. * @param {number} value
  411. * @return {number}
  412. */
  413. // FIXME Precision of float value
  414. listProto.indexOf = function (dim, value) {
  415. var storage = this._storage;
  416. var dimData = storage[dim];
  417. var indices = this.indices;
  418. if (dimData) {
  419. for (var i = 0, len = indices.length; i < len; i++) {
  420. var rawIndex = indices[i];
  421. if (dimData[rawIndex] === value) {
  422. return i;
  423. }
  424. }
  425. }
  426. return -1;
  427. };
  428. /**
  429. * Retreive the index with given name
  430. * @param {number} idx
  431. * @param {number} name
  432. * @return {number}
  433. */
  434. listProto.indexOfName = function (name) {
  435. var indices = this.indices;
  436. var nameList = this._nameList;
  437. for (var i = 0, len = indices.length; i < len; i++) {
  438. var rawIndex = indices[i];
  439. if (nameList[rawIndex] === name) {
  440. return i;
  441. }
  442. }
  443. return -1;
  444. };
  445. /**
  446. * Retreive the index with given raw data index
  447. * @param {number} idx
  448. * @param {number} name
  449. * @return {number}
  450. */
  451. listProto.indexOfRawIndex = function (rawIndex) {
  452. // Indices are ascending
  453. var indices = this.indices; // If rawIndex === dataIndex
  454. var rawDataIndex = indices[rawIndex];
  455. if (rawDataIndex != null && rawDataIndex === rawIndex) {
  456. return rawIndex;
  457. }
  458. var left = 0;
  459. var right = indices.length - 1;
  460. while (left <= right) {
  461. var mid = (left + right) / 2 | 0;
  462. if (indices[mid] < rawIndex) {
  463. left = mid + 1;
  464. } else if (indices[mid] > rawIndex) {
  465. right = mid - 1;
  466. } else {
  467. return mid;
  468. }
  469. }
  470. return -1;
  471. };
  472. /**
  473. * Retreive the index of nearest value
  474. * @param {string} dim
  475. * @param {number} value
  476. * @param {boolean} stack If given value is after stacked
  477. * @param {number} [maxDistance=Infinity]
  478. * @return {Array.<number>} Considere multiple points has the same value.
  479. */
  480. listProto.indicesOfNearest = function (dim, value, stack, maxDistance) {
  481. var storage = this._storage;
  482. var dimData = storage[dim];
  483. var nearestIndices = [];
  484. if (!dimData) {
  485. return nearestIndices;
  486. }
  487. if (maxDistance == null) {
  488. maxDistance = Infinity;
  489. }
  490. var minDist = Number.MAX_VALUE;
  491. var minDiff = -1;
  492. for (var i = 0, len = this.count(); i < len; i++) {
  493. var diff = value - this.get(dim, i, stack);
  494. var dist = Math.abs(diff);
  495. if (diff <= maxDistance && dist <= minDist) {
  496. // For the case of two data are same on xAxis, which has sequence data.
  497. // Show the nearest index
  498. // https://github.com/ecomfe/echarts/issues/2869
  499. if (dist < minDist || diff >= 0 && minDiff < 0) {
  500. minDist = dist;
  501. minDiff = diff;
  502. nearestIndices.length = 0;
  503. }
  504. nearestIndices.push(i);
  505. }
  506. }
  507. return nearestIndices;
  508. };
  509. /**
  510. * Get raw data index
  511. * @param {number} idx
  512. * @return {number}
  513. */
  514. listProto.getRawIndex = function (idx) {
  515. var rawIdx = this.indices[idx];
  516. return rawIdx == null ? -1 : rawIdx;
  517. };
  518. /**
  519. * Get raw data item
  520. * @param {number} idx
  521. * @return {number}
  522. */
  523. listProto.getRawDataItem = function (idx) {
  524. return this._rawData.getItem(this.getRawIndex(idx));
  525. };
  526. /**
  527. * @param {number} idx
  528. * @param {boolean} [notDefaultIdx=false]
  529. * @return {string}
  530. */
  531. listProto.getName = function (idx) {
  532. return this._nameList[this.indices[idx]] || '';
  533. };
  534. /**
  535. * @param {number} idx
  536. * @param {boolean} [notDefaultIdx=false]
  537. * @return {string}
  538. */
  539. listProto.getId = function (idx) {
  540. return this._idList[this.indices[idx]] || this.getRawIndex(idx) + '';
  541. };
  542. function normalizeDimensions(dimensions) {
  543. if (!zrUtil.isArray(dimensions)) {
  544. dimensions = [dimensions];
  545. }
  546. return dimensions;
  547. }
  548. /**
  549. * Data iteration
  550. * @param {string|Array.<string>}
  551. * @param {Function} cb
  552. * @param {boolean} [stack=false]
  553. * @param {*} [context=this]
  554. *
  555. * @example
  556. * list.each('x', function (x, idx) {});
  557. * list.each(['x', 'y'], function (x, y, idx) {});
  558. * list.each(function (idx) {})
  559. */
  560. listProto.each = function (dims, cb, stack, context) {
  561. if (typeof dims === 'function') {
  562. context = stack;
  563. stack = cb;
  564. cb = dims;
  565. dims = [];
  566. }
  567. dims = zrUtil.map(normalizeDimensions(dims), this.getDimension, this);
  568. var value = [];
  569. var dimSize = dims.length;
  570. var indices = this.indices;
  571. context = context || this;
  572. for (var i = 0; i < indices.length; i++) {
  573. // Simple optimization
  574. switch (dimSize) {
  575. case 0:
  576. cb.call(context, i);
  577. break;
  578. case 1:
  579. cb.call(context, this.get(dims[0], i, stack), i);
  580. break;
  581. case 2:
  582. cb.call(context, this.get(dims[0], i, stack), this.get(dims[1], i, stack), i);
  583. break;
  584. default:
  585. for (var k = 0; k < dimSize; k++) {
  586. value[k] = this.get(dims[k], i, stack);
  587. } // Index
  588. value[k] = i;
  589. cb.apply(context, value);
  590. }
  591. }
  592. };
  593. /**
  594. * Data filter
  595. * @param {string|Array.<string>}
  596. * @param {Function} cb
  597. * @param {boolean} [stack=false]
  598. * @param {*} [context=this]
  599. */
  600. listProto.filterSelf = function (dimensions, cb, stack, context) {
  601. if (typeof dimensions === 'function') {
  602. context = stack;
  603. stack = cb;
  604. cb = dimensions;
  605. dimensions = [];
  606. }
  607. dimensions = zrUtil.map(normalizeDimensions(dimensions), this.getDimension, this);
  608. var newIndices = [];
  609. var value = [];
  610. var dimSize = dimensions.length;
  611. var indices = this.indices;
  612. context = context || this;
  613. for (var i = 0; i < indices.length; i++) {
  614. var keep; // Simple optimization
  615. if (!dimSize) {
  616. keep = cb.call(context, i);
  617. } else if (dimSize === 1) {
  618. keep = cb.call(context, this.get(dimensions[0], i, stack), i);
  619. } else {
  620. for (var k = 0; k < dimSize; k++) {
  621. value[k] = this.get(dimensions[k], i, stack);
  622. }
  623. value[k] = i;
  624. keep = cb.apply(context, value);
  625. }
  626. if (keep) {
  627. newIndices.push(indices[i]);
  628. }
  629. }
  630. this.indices = newIndices; // Reset data extent
  631. this._extent = {};
  632. return this;
  633. };
  634. /**
  635. * Data mapping to a plain array
  636. * @param {string|Array.<string>} [dimensions]
  637. * @param {Function} cb
  638. * @param {boolean} [stack=false]
  639. * @param {*} [context=this]
  640. * @return {Array}
  641. */
  642. listProto.mapArray = function (dimensions, cb, stack, context) {
  643. if (typeof dimensions === 'function') {
  644. context = stack;
  645. stack = cb;
  646. cb = dimensions;
  647. dimensions = [];
  648. }
  649. var result = [];
  650. this.each(dimensions, function () {
  651. result.push(cb && cb.apply(this, arguments));
  652. }, stack, context);
  653. return result;
  654. };
  655. function cloneListForMapAndSample(original, excludeDimensions) {
  656. var allDimensions = original.dimensions;
  657. var list = new List(zrUtil.map(allDimensions, original.getDimensionInfo, original), original.hostModel); // FIXME If needs stackedOn, value may already been stacked
  658. transferProperties(list, original);
  659. var storage = list._storage = {};
  660. var originalStorage = original._storage; // Init storage
  661. for (var i = 0; i < allDimensions.length; i++) {
  662. var dim = allDimensions[i];
  663. var dimStore = originalStorage[dim];
  664. if (zrUtil.indexOf(excludeDimensions, dim) >= 0) {
  665. storage[dim] = new dimStore.constructor(originalStorage[dim].length);
  666. } else {
  667. // Direct reference for other dimensions
  668. storage[dim] = originalStorage[dim];
  669. }
  670. }
  671. return list;
  672. }
  673. /**
  674. * Data mapping to a new List with given dimensions
  675. * @param {string|Array.<string>} dimensions
  676. * @param {Function} cb
  677. * @param {boolean} [stack=false]
  678. * @param {*} [context=this]
  679. * @return {Array}
  680. */
  681. listProto.map = function (dimensions, cb, stack, context) {
  682. dimensions = zrUtil.map(normalizeDimensions(dimensions), this.getDimension, this);
  683. var list = cloneListForMapAndSample(this, dimensions); // Following properties are all immutable.
  684. // So we can reference to the same value
  685. var indices = list.indices = this.indices;
  686. var storage = list._storage;
  687. var tmpRetValue = [];
  688. this.each(dimensions, function () {
  689. var idx = arguments[arguments.length - 1];
  690. var retValue = cb && cb.apply(this, arguments);
  691. if (retValue != null) {
  692. // a number
  693. if (typeof retValue === 'number') {
  694. tmpRetValue[0] = retValue;
  695. retValue = tmpRetValue;
  696. }
  697. for (var i = 0; i < retValue.length; i++) {
  698. var dim = dimensions[i];
  699. var dimStore = storage[dim];
  700. var rawIdx = indices[idx];
  701. if (dimStore) {
  702. dimStore[rawIdx] = retValue[i];
  703. }
  704. }
  705. }
  706. }, stack, context);
  707. return list;
  708. };
  709. /**
  710. * Large data down sampling on given dimension
  711. * @param {string} dimension
  712. * @param {number} rate
  713. * @param {Function} sampleValue
  714. * @param {Function} sampleIndex Sample index for name and id
  715. */
  716. listProto.downSample = function (dimension, rate, sampleValue, sampleIndex) {
  717. var list = cloneListForMapAndSample(this, [dimension]);
  718. var storage = this._storage;
  719. var targetStorage = list._storage;
  720. var originalIndices = this.indices;
  721. var indices = list.indices = [];
  722. var frameValues = [];
  723. var frameIndices = [];
  724. var frameSize = Math.floor(1 / rate);
  725. var dimStore = targetStorage[dimension];
  726. var len = this.count(); // Copy data from original data
  727. for (var i = 0; i < storage[dimension].length; i++) {
  728. targetStorage[dimension][i] = storage[dimension][i];
  729. }
  730. for (var i = 0; i < len; i += frameSize) {
  731. // Last frame
  732. if (frameSize > len - i) {
  733. frameSize = len - i;
  734. frameValues.length = frameSize;
  735. }
  736. for (var k = 0; k < frameSize; k++) {
  737. var idx = originalIndices[i + k];
  738. frameValues[k] = dimStore[idx];
  739. frameIndices[k] = idx;
  740. }
  741. var value = sampleValue(frameValues);
  742. var idx = frameIndices[sampleIndex(frameValues, value) || 0]; // Only write value on the filtered data
  743. dimStore[idx] = value;
  744. indices.push(idx);
  745. }
  746. return list;
  747. };
  748. /**
  749. * Get model of one data item.
  750. *
  751. * @param {number} idx
  752. */
  753. // FIXME Model proxy ?
  754. listProto.getItemModel = function (idx) {
  755. var hostModel = this.hostModel;
  756. idx = this.indices[idx];
  757. return new Model(this._rawData.getItem(idx), hostModel, hostModel && hostModel.ecModel);
  758. };
  759. /**
  760. * Create a data differ
  761. * @param {module:echarts/data/List} otherList
  762. * @return {module:echarts/data/DataDiffer}
  763. */
  764. listProto.diff = function (otherList) {
  765. var idList = this._idList;
  766. var otherIdList = otherList && otherList._idList;
  767. var val; // Use prefix to avoid index to be the same as otherIdList[idx],
  768. // which will cause weird udpate animation.
  769. var prefix = 'e\0\0';
  770. return new DataDiffer(otherList ? otherList.indices : [], this.indices, function (idx) {
  771. return (val = otherIdList[idx]) != null ? val : prefix + idx;
  772. }, function (idx) {
  773. return (val = idList[idx]) != null ? val : prefix + idx;
  774. });
  775. };
  776. /**
  777. * Get visual property.
  778. * @param {string} key
  779. */
  780. listProto.getVisual = function (key) {
  781. var visual = this._visual;
  782. return visual && visual[key];
  783. };
  784. /**
  785. * Set visual property
  786. * @param {string|Object} key
  787. * @param {*} [value]
  788. *
  789. * @example
  790. * setVisual('color', color);
  791. * setVisual({
  792. * 'color': color
  793. * });
  794. */
  795. listProto.setVisual = function (key, val) {
  796. if (isObject(key)) {
  797. for (var name in key) {
  798. if (key.hasOwnProperty(name)) {
  799. this.setVisual(name, key[name]);
  800. }
  801. }
  802. return;
  803. }
  804. this._visual = this._visual || {};
  805. this._visual[key] = val;
  806. };
  807. /**
  808. * Set layout property.
  809. * @param {string|Object} key
  810. * @param {*} [val]
  811. */
  812. listProto.setLayout = function (key, val) {
  813. if (isObject(key)) {
  814. for (var name in key) {
  815. if (key.hasOwnProperty(name)) {
  816. this.setLayout(name, key[name]);
  817. }
  818. }
  819. return;
  820. }
  821. this._layout[key] = val;
  822. };
  823. /**
  824. * Get layout property.
  825. * @param {string} key.
  826. * @return {*}
  827. */
  828. listProto.getLayout = function (key) {
  829. return this._layout[key];
  830. };
  831. /**
  832. * Get layout of single data item
  833. * @param {number} idx
  834. */
  835. listProto.getItemLayout = function (idx) {
  836. return this._itemLayouts[idx];
  837. };
  838. /**
  839. * Set layout of single data item
  840. * @param {number} idx
  841. * @param {Object} layout
  842. * @param {boolean=} [merge=false]
  843. */
  844. listProto.setItemLayout = function (idx, layout, merge) {
  845. this._itemLayouts[idx] = merge ? zrUtil.extend(this._itemLayouts[idx] || {}, layout) : layout;
  846. };
  847. /**
  848. * Clear all layout of single data item
  849. */
  850. listProto.clearItemLayouts = function () {
  851. this._itemLayouts.length = 0;
  852. };
  853. /**
  854. * Get visual property of single data item
  855. * @param {number} idx
  856. * @param {string} key
  857. * @param {boolean} [ignoreParent=false]
  858. */
  859. listProto.getItemVisual = function (idx, key, ignoreParent) {
  860. var itemVisual = this._itemVisuals[idx];
  861. var val = itemVisual && itemVisual[key];
  862. if (val == null && !ignoreParent) {
  863. // Use global visual property
  864. return this.getVisual(key);
  865. }
  866. return val;
  867. };
  868. /**
  869. * Set visual property of single data item
  870. *
  871. * @param {number} idx
  872. * @param {string|Object} key
  873. * @param {*} [value]
  874. *
  875. * @example
  876. * setItemVisual(0, 'color', color);
  877. * setItemVisual(0, {
  878. * 'color': color
  879. * });
  880. */
  881. listProto.setItemVisual = function (idx, key, value) {
  882. var itemVisual = this._itemVisuals[idx] || {};
  883. this._itemVisuals[idx] = itemVisual;
  884. if (isObject(key)) {
  885. for (var name in key) {
  886. if (key.hasOwnProperty(name)) {
  887. itemVisual[name] = key[name];
  888. }
  889. }
  890. return;
  891. }
  892. itemVisual[key] = value;
  893. };
  894. /**
  895. * Clear itemVisuals and list visual.
  896. */
  897. listProto.clearAllVisual = function () {
  898. this._visual = {};
  899. this._itemVisuals = [];
  900. };
  901. var setItemDataAndSeriesIndex = function (child) {
  902. child.seriesIndex = this.seriesIndex;
  903. child.dataIndex = this.dataIndex;
  904. child.dataType = this.dataType;
  905. };
  906. /**
  907. * Set graphic element relative to data. It can be set as null
  908. * @param {number} idx
  909. * @param {module:zrender/Element} [el]
  910. */
  911. listProto.setItemGraphicEl = function (idx, el) {
  912. var hostModel = this.hostModel;
  913. if (el) {
  914. // Add data index and series index for indexing the data by element
  915. // Useful in tooltip
  916. el.dataIndex = idx;
  917. el.dataType = this.dataType;
  918. el.seriesIndex = hostModel && hostModel.seriesIndex;
  919. if (el.type === 'group') {
  920. el.traverse(setItemDataAndSeriesIndex, el);
  921. }
  922. }
  923. this._graphicEls[idx] = el;
  924. };
  925. /**
  926. * @param {number} idx
  927. * @return {module:zrender/Element}
  928. */
  929. listProto.getItemGraphicEl = function (idx) {
  930. return this._graphicEls[idx];
  931. };
  932. /**
  933. * @param {Function} cb
  934. * @param {*} context
  935. */
  936. listProto.eachItemGraphicEl = function (cb, context) {
  937. zrUtil.each(this._graphicEls, function (el, idx) {
  938. if (el) {
  939. cb && cb.call(context, el, idx);
  940. }
  941. });
  942. };
  943. /**
  944. * Shallow clone a new list except visual and layout properties, and graph elements.
  945. * New list only change the indices.
  946. */
  947. listProto.cloneShallow = function () {
  948. var dimensionInfoList = zrUtil.map(this.dimensions, this.getDimensionInfo, this);
  949. var list = new List(dimensionInfoList, this.hostModel); // FIXME
  950. list._storage = this._storage;
  951. transferProperties(list, this); // Clone will not change the data extent and indices
  952. list.indices = this.indices.slice();
  953. if (this._extent) {
  954. list._extent = zrUtil.extend({}, this._extent);
  955. }
  956. return list;
  957. };
  958. /**
  959. * Wrap some method to add more feature
  960. * @param {string} methodName
  961. * @param {Function} injectFunction
  962. */
  963. listProto.wrapMethod = function (methodName, injectFunction) {
  964. var originalMethod = this[methodName];
  965. if (typeof originalMethod !== 'function') {
  966. return;
  967. }
  968. this.__wrappedMethods = this.__wrappedMethods || [];
  969. this.__wrappedMethods.push(methodName);
  970. this[methodName] = function () {
  971. var res = originalMethod.apply(this, arguments);
  972. return injectFunction.apply(this, [res].concat(zrUtil.slice(arguments)));
  973. };
  974. }; // Methods that create a new list based on this list should be listed here.
  975. // Notice that those method should `RETURN` the new list.
  976. listProto.TRANSFERABLE_METHODS = ['cloneShallow', 'downSample', 'map']; // Methods that change indices of this list should be listed here.
  977. listProto.CHANGABLE_METHODS = ['filterSelf'];
  978. var _default = List;
  979. module.exports = _default;