Skip to content

fix(data): detect sparse object keys. close #11322#21699

Open
rahul37wallst-sudo wants to merge 1 commit into
apache:masterfrom
rahul37wallst-sudo:fix-11322-object-row-dimensions
Open

fix(data): detect sparse object keys. close #11322#21699
rahul37wallst-sudo wants to merge 1 commit into
apache:masterfrom
rahul37wallst-sudo:fix-11322-object-row-dimensions

Conversation

@rahul37wallst-sudo

Copy link
Copy Markdown

Brief Information

This pull request is in the type of:

  • bug fixing
  • new feature
  • others

What does this PR do?

Collects dimensions from all object-row dataset entries so sparse fields referenced by series.encode map to the correct data.

Fixed issues

Details

Before: What was the problem?

ECharts detected object-row dataset dimensions using only the first non-empty object.

For data such as:

dataset: {
    source: [
        { timestamp: 1568191020000, ECM: 26311.466666666667 },
        {
            timestamp: 1568191140000,
            ECM: 1666775.3333333333,
            GSWY: 1332.5333333333333
        }
    ]
}

the GSWY dimension was not detected because it was absent from the first row.

Consequently, a bar series configured with:

encode: {
    x: 'timestamp',
    y: 'GSWY'
}

could silently map its y-axis data to another detected dimension, such as ECM, and render incorrect values.

After: How does it behave after the fixing?

Object-row dimension detection now collects each unique property across all rows while preserving first-seen order.

As a result, GSWY is registered as a dataset dimension even when it is absent from earlier rows. The corresponding series correctly maps its y-axis data to GSWY; rows without that property remain empty instead of using values from another dimension.

Regression tests cover both source dimension detection and the stacked bar chart behavior from the issue.

Document Info

One of the following should be checked.

  • This PR doesn't relate to document changes
  • The document should be updated later
  • The document changes have been made in apache/echarts-doc#xxx

Misc

Security Checking

  • This PR uses security-sensitive Web APIs.

ZRender Changes

  • This PR depends on ZRender changes (ecomfe/zrender#xxx).

Related test cases or examples to use the new APIs

Regression coverage was added in:

  • test/ut/spec/data/SeriesData.test.ts
  • test/ut/spec/series/bar.test.ts

Merging options

  • Please squash the commits into a single one when merging.

Other information

Validated with:

npx jest --config test/ut/jest.config.cjs --coverage=false --runInBand --runTestsByPath test/ut/spec/data/SeriesData.test.ts test/ut/spec/series/bar.test.ts
npx eslint src/data/Source.ts test/ut/spec/data/SeriesData.test.ts test/ut/spec/series/bar.test.ts
npm run checktype
npm run lint
git diff --check

@echarts-bot

echarts-bot Bot commented Jul 17, 2026

Copy link
Copy Markdown

Thanks for your contribution!
The community will review it ASAP. In the meanwhile, please checkout the coding standard and Wiki about How to make a pull request.

Please DO NOT commit the files in dist, i18n, and ssr/client/dist folders in a non-release pull request. These folders are for release use only.

Comment thread src/data/Source.ts
Comment on lines +404 to 418
const dimensionNameMap = createHashMap<true, DimensionName>();
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);
}
});
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for fixing this issue. I agree that collecting keys from all object rows is necessary to handle sparse fields correctly, but it also adds an O(total number of object keys) pre-scan during source initialization when dimensions is not explicitly provided.

Could you please add a small performance check to verify that this does not introduce a noticeable overhead for large datasets? It would be useful to compare:

  • the previous behavior (reading only the first non-empty object)
  • the new full-scan behavior
  • the path where dataset.dimensions is explicitly provided

For example, testing with around 100k rows would be helpful, covering both stable object shapes and sparse keys that appear only in later rows. Measuring setOption or source creation time should be sufficient. This would help us assess the practical impact of the additional scan.

Also, the current implementation creates a keys(obj) array and uses a callback for every row. If this shows up in the benchmark, could we avoid the intermediate allocation and callback overhead with something like:

for (const name in obj) {
    if (hasOwn(obj, name) && !dimensionNameMap.get(name)) {
        dimensionNameMap.set(name, true);
        dimensionsDefine.push(name);
    }
}

This preserves the intended semantics, collecting every field while retaining first-seen order, while potentially reducing allocations and per-row overhead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants