From db7241a62672e2151a18a6c8939a22e7d1bb4725 Mon Sep 17 00:00:00 2001 From: tiwarir Date: Fri, 17 Jul 2026 12:09:43 -0400 Subject: [PATCH 1/2] fix(data): detect sparse object keys. close #11322 --- src/data/Source.ts | 21 ++++++--- test/ut/spec/data/SeriesData.test.ts | 14 ++++++ test/ut/spec/series/bar.test.ts | 65 ++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 5 deletions(-) create mode 100644 test/ut/spec/series/bar.test.ts diff --git a/src/data/Source.ts b/src/data/Source.ts index adbeeaf551..69d832f807 100644 --- a/src/data/Source.ts +++ b/src/data/Source.ts @@ -401,12 +401,23 @@ function determineSourceDimensions( } function objectRowsCollectDimensions(data: OptionSourceDataObjectRows): DimensionDefinitionLoose[] { - let firstIndex = 0; - let obj; - while (firstIndex < data.length && !(obj = data[firstIndex++])) {} // jshint ignore: line - if (obj) { - return keys(obj); + const dimensionNameMap = createHashMap(); + const dimensionsDefine: DimensionDefinitionLoose[] = []; + + for (let i = 0; i < data.length; i++) { + const obj = data[i]; + if (!obj) { + continue; + } + each(keys(obj), function (name) { + if (!dimensionNameMap.get(name)) { + dimensionNameMap.set(name, true); + dimensionsDefine.push(name); + } + }); } + + return dimensionsDefine.length ? dimensionsDefine : void 0; } // Consider dimensions defined like ['A', 'price', 'B', 'price', 'C', 'price'], diff --git a/test/ut/spec/data/SeriesData.test.ts b/test/ut/spec/data/SeriesData.test.ts index d71fb3547f..5962abeda8 100644 --- a/test/ut/spec/data/SeriesData.test.ts +++ b/test/ut/spec/data/SeriesData.test.ts @@ -25,6 +25,7 @@ import { createSourceFromSeriesDataOption, Source, createSource } from '@/src/da import { OptionDataItemObject, OptionDataValue, SOURCE_FORMAT_ARRAY_ROWS, + SOURCE_FORMAT_OBJECT_ROWS, SOURCE_FORMAT_ORIGINAL } from '@/src/util/types'; import SeriesDimensionDefine from '@/src/data/SeriesDimensionDefine'; import OrdinalMeta from '@/src/data/OrdinalMeta'; @@ -206,6 +207,19 @@ describe('SeriesData', function () { expect(source.dimensionsDefine[0].type).toEqual('ordinal'); }); + it('should collect dimensions across all object rows', function () { + const source = createSource([ + { timestamp: 1568191020000, ECM: 26311.466666666667 }, + { timestamp: 1568191140000, ECM: 1666775.3333333333, GSWY: 1332.5333333333333 } + ], { + dimensions: null, + seriesLayoutBy: null, + sourceHeader: false + }, SOURCE_FORMAT_OBJECT_ROWS); + + expect(source.dimensionsDefine.map(dim => dim.name)).toEqual(['timestamp', 'ECM', 'GSWY']); + }); + function createStore() { const provider = new DefaultDataProvider([['A', 15], ['B', 25], ['C', 35]]); const store = new DataStore(); diff --git a/test/ut/spec/series/bar.test.ts b/test/ut/spec/series/bar.test.ts new file mode 100644 index 0000000000..73e703d2d9 --- /dev/null +++ b/test/ut/spec/series/bar.test.ts @@ -0,0 +1,65 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one +* or more contributor license agreements. See the NOTICE file +* distributed with this work for additional information +* regarding copyright ownership. The ASF licenses this file +* to you under the Apache License, Version 2.0 (the +* "License"); you may not use this file except in compliance +* with the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +import { createChart } from '../../core/utHelper'; + + +describe('bar', function () { + + it('should respect encoded object-row dimensions missing from earlier rows', function () { + const chart = createChart({width: 400, height: 300}); + + try { + chart.setOption({ + animation: false, + dataset: { + source: [ + { timestamp: 1568191020000, ECM: 26311.466666666667 }, + { timestamp: 1568191140000, ECM: 1666775.3333333333, GSWY: 1332.5333333333333 } + ] + }, + xAxis: { type: 'time' }, + yAxis: { type: 'value' }, + series: [{ + type: 'bar', + name: 'ECM', + encode: { x: 'timestamp', y: 'ECM' }, + stack: 'one' + }, { + type: 'bar', + name: 'GSWY', + encode: { x: 'timestamp', y: 'GSWY' }, + stack: 'one' + }] + }); + + const series = (chart as any).getModel().getSeriesByIndex(1); + const data = series.getData(); + const yDim = data.mapDimension('y'); + + expect(yDim).toBe('GSWY'); + expect(isNaN(data.get(yDim, 0))).toBe(true); + expect(data.get(yDim, 1)).toBe(1332.5333333333333); + } + finally { + chart.dispose(); + } + }); + +}); From b6ace83a5b057a73201e3ed17afd1e23dc528e11 Mon Sep 17 00:00:00 2001 From: tiwarir Date: Wed, 22 Jul 2026 11:34:41 -0400 Subject: [PATCH 2/2] perf(data): avoid object key scan allocations. close #11322 --- src/data/Source.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/data/Source.ts b/src/data/Source.ts index 69d832f807..a79a454deb 100644 --- a/src/data/Source.ts +++ b/src/data/Source.ts @@ -19,7 +19,7 @@ import { isTypedArray, HashMap, clone, createHashMap, isArray, isObject, isArrayLike, - hasOwn, assert, each, map, isNumber, isString, keys + hasOwn, assert, each, map, isNumber, isString } from 'zrender/src/core/util'; import { SourceFormat, SeriesLayoutBy, DimensionDefinition, @@ -409,12 +409,12 @@ function objectRowsCollectDimensions(data: OptionSourceDataObjectRows): Dimensio if (!obj) { continue; } - each(keys(obj), function (name) { - if (!dimensionNameMap.get(name)) { + for (const name in obj) { + if (hasOwn(obj, name) && !dimensionNameMap.get(name)) { dimensionNameMap.set(name, true); dimensionsDefine.push(name); } - }); + } } return dimensionsDefine.length ? dimensionsDefine : void 0;