}
```
+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
}
}
-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;
}
/**
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() {