Skip to content

Commit

Permalink
parsing: support dot(s) in object keys (#10517)
Browse files Browse the repository at this point in the history
  • Loading branch information
kurkle committed Jul 26, 2022
1 parent 144a6c0 commit a4114e8
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 16 deletions.
18 changes: 17 additions & 1 deletion docs/general/data-structures.md
Expand Up @@ -85,11 +85,27 @@ options: {
}
```

If the key contains a dot, it needs to be escaped with a double slash:

```javascript
type: 'line',
data: {
datasets: [{
data: [{ 'data.key': 'one', 'data.value': 20 }, { 'data.key': 'two', 'data.value': 30 }]
}]
},
options: {
parsing: {
xAxisKey: 'data\\.key',
yAxisKey: 'data\\.value'
}
}
```

:::warning
When using object notation in a radar chart you still need a labels array with labels for the chart to show correctly.
:::


## Object

```javascript
Expand Down
57 changes: 42 additions & 15 deletions src/helpers/helpers.core.js
Expand Up @@ -293,25 +293,52 @@ export function _deprecated(scope, value, previous, current) {
}
}

const emptyString = '';
const dot = '.';
function indexOfDotOrLength(key, start) {
const idx = key.indexOf(dot, start);
return idx === -1 ? key.length : idx;
}
// resolveObjectKey resolver cache
const keyResolvers = {
// Chart.helpers.core resolveObjectKey should resolve empty key to root object
'': v => v,
// default resolvers
x: o => o.x,
y: o => o.y
};

export function resolveObjectKey(obj, key) {
if (key === emptyString) {
const resolver = keyResolvers[key] || (keyResolvers[key] = _getKeyResolver(key));
return resolver(obj);
}

function _getKeyResolver(key) {
const keys = _splitKey(key);
return obj => {
for (const k of keys) {
if (k === '') {
// For backward compatibility:
// Chart.helpers.core resolveObjectKey should break at empty key
break;
}
obj = obj && obj[k];
}
return obj;
};
}

/**
* @private
*/
export function _splitKey(key) {
const parts = key.split('.');
const keys = [];
let tmp = '';
for (const part of parts) {
tmp += part;
if (tmp.endsWith('\\')) {
tmp = tmp.slice(0, -1) + '.';
} else {
keys.push(tmp);
tmp = '';
}
}
let pos = 0;
let idx = indexOfDotOrLength(key, pos);
while (obj && idx > pos) {
obj = obj[key.slice(pos, idx)];
pos = idx + 1;
idx = indexOfDotOrLength(key, pos);
}
return obj;
return keys;
}

/**
Expand Down
38 changes: 38 additions & 0 deletions test/specs/helpers.core.tests.js
Expand Up @@ -456,6 +456,44 @@ describe('Chart.helpers.core', function() {
expect(() => helpers.resolveObjectKey({}, true)).toThrow();
expect(() => helpers.resolveObjectKey({}, 1)).toThrow();
});
it('should allow escaping dot symbol', function() {
expect(helpers.resolveObjectKey({'test.dot': 10}, 'test\\.dot')).toEqual(10);
expect(helpers.resolveObjectKey({test: {dot: 10}}, 'test\\.dot')).toEqual(undefined);
});
it('should allow nested keys with a dot', function() {
expect(helpers.resolveObjectKey({
a: {
'bb.ccc': 'works',
bb: {
ccc: 'fails'
}
}
}, 'a.bb\\.ccc')).toEqual('works');
});

});

describe('_splitKey', function() {
it('should return array with one entry for string without a dot', function() {
expect(helpers._splitKey('')).toEqual(['']);
expect(helpers._splitKey('test')).toEqual(['test']);
const asciiWithoutDot = ' !"#$%&\'()*+,-/0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
expect(helpers._splitKey(asciiWithoutDot)).toEqual([asciiWithoutDot]);
});

it('should split on dot', function() {
expect(helpers._splitKey('test1.test2')).toEqual(['test1', 'test2']);
expect(helpers._splitKey('a.b.c')).toEqual(['a', 'b', 'c']);
expect(helpers._splitKey('a.b.')).toEqual(['a', 'b', '']);
expect(helpers._splitKey('a..c')).toEqual(['a', '', 'c']);
});

it('should preserve escaped dot', function() {
expect(helpers._splitKey('test1\\.test2')).toEqual(['test1.test2']);
expect(helpers._splitKey('a\\.b.c')).toEqual(['a.b', 'c']);
expect(helpers._splitKey('a.b\\.c')).toEqual(['a', 'b.c']);
expect(helpers._splitKey('a.\\.c')).toEqual(['a', '.c']);
});
});

describe('setsEqual', function() {
Expand Down

0 comments on commit a4114e8

Please sign in to comment.