Browse Source

Keep ordered keys as separate property in pivot format returned by convert methods. (#2314)

This fixes the issue where targets order would not be kept when some of
them have numerical values (ex: "1", "2", ..).
pull/2333/merge
Anthony Pessy 7 years ago committed by Yoshiya Hinosawa
parent
commit
d867f455b5
  1. 465
      spec/data.convert.js
  2. 54
      src/data.convert.js

465
spec/data.convert.js

@ -1,97 +1,392 @@
import c3 from '../src';
const $$ = c3.chart.internal.fn;
$$.d3 = require("d3");
describe('$$.convertColumnsToData', () => {
it('converts column data to normalized data', () => {
const data = $$.convertColumnsToData([
["cat1", "a", "b", "c", "d"],
["data1", 30, 200, 100, 400],
["cat2", "b", "a", "c", "d", "e", "f"],
["data2", 400, 60, 200, 800, 10, 10]
]);
expect(data).toEqual([{
cat1: 'a',
data1: 30,
cat2: 'b',
data2: 400
}, {
cat1: 'b',
data1: 200,
cat2: 'a',
data2: 60
}, {
cat1: 'c',
data1: 100,
cat2: 'c',
data2: 200
}, {
cat1: 'd',
data1: 400,
cat2: 'd',
data2: 800
}, {
cat2: 'e',
data2: 10
}, {
cat2: 'f',
data2: 10
}]);
describe('data.convert', () => {
describe('$$.convertColumnsToData', () => {
it('converts column data to normalized data', () => {
const data = $$.convertColumnsToData([
["cat1", "a", "b", "c", "d"],
["data1", 30, 200, 100, 400],
["cat2", "b", "a", "c", "d", "e", "f"],
["data2", 400, 60, 200, 800, 10, 10]
]);
expect(data).toEqual({
keys: [ 'cat1', 'data1', 'cat2', 'data2' ],
rows: [{
cat1: 'a',
data1: 30,
cat2: 'b',
data2: 400
}, {
cat1: 'b',
data1: 200,
cat2: 'a',
data2: 60
}, {
cat1: 'c',
data1: 100,
cat2: 'c',
data2: 200
}, {
cat1: 'd',
data1: 400,
cat2: 'd',
data2: 800
}, {
cat2: 'e',
data2: 10
}, {
cat2: 'f',
data2: 10
}]
});
});
it('throws when the column data contains undefined', () => {
expect(() => $$.convertColumnsToData([
["cat1", "a", "b", "c", "d"],
["data1", undefined]
])).toThrowError(Error, /Source data is missing a component/);
});
});
it('throws when the column data contains undefined', () => {
expect(() => $$.convertColumnsToData([
["cat1", "a", "b", "c", "d"],
["data1", undefined]
])).toThrowError(Error, /Source data is missing a component/);
describe('$$.convertRowsToData', () => {
it('converts the row data to normalized data', () => {
const data = $$.convertRowsToData([
['data1', 'data2', 'data3'],
[90, 120, 300],
[40, 160, 240],
[50, 200, 290],
[120, 160, 230],
[80, 130, 300],
[90, 220, 320]
]);
expect(data).toEqual({
keys: ['data1', 'data2', 'data3'],
rows: [{
data1: 90,
data2: 120,
data3: 300
}, {
data1: 40,
data2: 160,
data3: 240
}, {
data1: 50,
data2: 200,
data3: 290
}, {
data1: 120,
data2: 160,
data3: 230
}, {
data1: 80,
data2: 130,
data3: 300
}, {
data1: 90,
data2: 220,
data3: 320
}]
});
});
it('throws when the row data contains undefined', () => {
expect(() => $$.convertRowsToData([
['data1', 'data2', 'data3'],
[40, 160, 240],
[90, 120, undefined]
])).toThrowError(Error, /Source data is missing a component/);
});
});
});
describe('$$.convertRowsToData', () => {
it('converts the row data to normalized data', () => {
const data = $$.convertRowsToData([
['data1', 'data2', 'data3'],
[90, 120, 300],
[40, 160, 240],
[50, 200, 290],
[120, 160, 230],
[80, 130, 300],
[90, 220, 320]
]);
expect(data).toEqual([{
data1: 90,
data2: 120,
data3: 300
}, {
data1: 40,
data2: 160,
data3: 240
}, {
data1: 50,
data2: 200,
data3: 290
}, {
data1: 120,
data2: 160,
data3: 230
}, {
data1: 80,
data2: 130,
data3: 300
}, {
data1: 90,
data2: 220,
data3: 320
}]);
describe('$$.convertCsvToData', () => {
it('converts the csv data to normalized data', () => {
const data = $$.convertCsvToData(`data1,data2,data3
90,120,300
40,160,240
50,200,290
120,160,230
80,130,300
90,220,320`);
expect(data).toEqual({
keys: ['data1', 'data2', 'data3'],
rows: [{
data1: '90',
data2: '120',
data3: '300'
}, {
data1: '40',
data2: '160',
data3: '240'
}, {
data1: '50',
data2: '200',
data3: '290'
}, {
data1: '120',
data2: '160',
data3: '230'
}, {
data1: '80',
data2: '130',
data3: '300'
}, {
data1: '90',
data2: '220',
data3: '320'
}]
});
});
it('converts one lined CSV data', () => {
const data = $$.convertCsvToData(`data1,data2,data3`);
expect(data).toEqual({
keys: ['data1', 'data2', 'data3'],
rows: [{
data1: null,
data2: null,
data3: null
}]
});
});
});
it('throws when the row data contains undefined', () => {
expect(() => $$.convertRowsToData([
['data1', 'data2', 'data3'],
[40, 160, 240],
[90, 120, undefined]
])).toThrowError(Error, /Source data is missing a component/);
describe('$$.convertTsvToData', () => {
it('converts the tsv data to normalized data', () => {
const data = $$.convertTsvToData(`data1\tdata2\tdata3
90\t120\t300
40\t160\t240
50\t200\t290
120\t160\t230
80\t130\t300
90\t220\t320`);
expect(data).toEqual({
keys: ['data1', 'data2', 'data3'],
rows: [{
data1: '90',
data2: '120',
data3: '300'
}, {
data1: '40',
data2: '160',
data3: '240'
}, {
data1: '50',
data2: '200',
data3: '290'
}, {
data1: '120',
data2: '160',
data3: '230'
}, {
data1: '80',
data2: '130',
data3: '300'
}, {
data1: '90',
data2: '220',
data3: '320'
}]
});
});
it('converts one lined TSV data', () => {
const data = $$.convertTsvToData(`data1\tdata2\tdata3`);
expect(data).toEqual({
keys: ['data1', 'data2', 'data3'],
rows: [{
data1: null,
data2: null,
data3: null
}]
});
});
});
describe('$$.convertDataToTargets', () => {
beforeEach(() => {
$$.cache = {};
$$.data = {
xs: []
};
$$.config = {
data_idConverter: (v) => v
};
});
it('converts the legacy data format into targets', () => {
const targets = $$.convertDataToTargets([ {
data1: 90,
data2: 120,
data3: 300
}, {
data1: 40,
data2: 160,
data3: 240
} ]);
expect(targets).toEqual([{
id: 'data1',
id_org: 'data1',
values: [ { x: 0, value: 90, id: 'data1', index: 0 }, { x: 1, value: 40, id: 'data1', index: 1 } ]
}, {
id: 'data2',
id_org: 'data2',
values: [ { x: 0, value: 120, id: 'data2', index: 0 }, { x: 1, value: 160, id: 'data2', index: 1 } ]
}, {
id: 'data3',
id_org: 'data3',
values: [ { x: 0, value: 300, id: 'data3', index: 0 }, { x: 1, value: 240, id: 'data3', index: 1 } ]
}]);
});
it('converts the data into targets', () => {
const targets = $$.convertDataToTargets({
keys: [ 'data1', 'data2', 'data3' ],
rows: [ {
data1: 90,
data2: 120,
data3: 300
}, {
data1: 40,
data2: 160,
data3: 240
} ]
});
expect(targets).toEqual([{
id: 'data1',
id_org: 'data1',
values: [ { x: 0, value: 90, id: 'data1', index: 0 }, { x: 1, value: 40, id: 'data1', index: 1 } ]
}, {
id: 'data2',
id_org: 'data2',
values: [ { x: 0, value: 120, id: 'data2', index: 0 }, { x: 1, value: 160, id: 'data2', index: 1 } ]
}, {
id: 'data3',
id_org: 'data3',
values: [ { x: 0, value: 300, id: 'data3', index: 0 }, { x: 1, value: 240, id: 'data3', index: 1 } ]
}]);
});
});
describe('$$.convertJsonToData', () => {
it('converts JSON as object (no keys provided)', () => {
const data = $$.convertJsonToData({
data1: [ 90, 40, 50, 120, 80, 90 ],
data2: [ 120, 160, 200, 160, 130, 220 ],
data3: [ 300, 240, 290, 230, 300, 320 ]
});
expect(data).toEqual({
keys: ['data1', 'data2', 'data3'],
rows: [{
data1: 90,
data2: 120,
data3: 300
}, {
data1: 40,
data2: 160,
data3: 240
}, {
data1: 50,
data2: 200,
data3: 290
}, {
data1: 120,
data2: 160,
data3: 230
}, {
data1: 80,
data2: 130,
data3: 300
}, {
data1: 90,
data2: 220,
data3: 320
}]
});
});
it('converts JSON as rows (keys provided)', () => {
const data = $$.convertJsonToData([{
data1: 90,
data2: 120,
data3: 300,
unused: 42
}, {
data1: 40,
data2: 160,
data3: 240,
unused: 42
}, {
data1: 50,
data2: 200,
data3: 290,
unused: 42
}, {
data1: 120,
data2: 160,
data3: 230,
unused: 42
}, {
data1: 80,
data2: 130,
data3: 300,
unused: 42
}, {
data1: 90,
data2: 220,
data3: 320,
unused: 42
}], {
value: [ 'data1', 'data2', 'data3' ]
});
expect(data).toEqual({
keys: ['data1', 'data2', 'data3'],
rows: [{
data1: 90,
data2: 120,
data3: 300
}, {
data1: 40,
data2: 160,
data3: 240
}, {
data1: 50,
data2: 200,
data3: 290
}, {
data1: 120,
data2: 160,
data3: 230
}, {
data1: 80,
data2: 130,
data3: 300
}, {
data1: 90,
data2: 220,
data3: 320
}]
});
});
});
});

54
src/data.convert.js

@ -1,5 +1,5 @@
import { c3_chart_internal_fn } from './core';
import { isValue, isUndefined, isDefined, notEmpty } from './util';
import { isValue, isUndefined, isDefined, notEmpty, isArray } from './util';
c3_chart_internal_fn.convertUrlToData = function (url, mimeType, headers, keys, done) {
var $$ = this, type = mimeType ? mimeType : 'csv';
@ -26,22 +26,20 @@ c3_chart_internal_fn.convertUrlToData = function (url, mimeType, headers, keys,
});
};
c3_chart_internal_fn.convertXsvToData = function (xsv, parser) {
var rows = parser(xsv), d;
if (rows.length === 1) {
d = [{}];
rows[0].forEach(function (id) {
d[0][id] = null;
});
var [ keys, ...rows ] = parser.parseRows(xsv);
if (rows.length === 0) {
return { keys, rows: [ keys.reduce((row, key) => Object.assign(row, { [key]: null }), {}) ] };
} else {
d = parser(xsv);
// [].concat() is to convert result into a plain array otherwise
// test is not happy because rows have properties.
return { keys, rows: [].concat(parser.parse(xsv)) };
}
return d;
};
c3_chart_internal_fn.convertCsvToData = function (csv) {
return this.convertXsvToData(csv, this.d3.csvParse);
return this.convertXsvToData(csv, { parse: this.d3.csvParse, parseRows: this.d3.csvParseRows });
};
c3_chart_internal_fn.convertTsvToData = function (tsv) {
return this.convertXsvToData(tsv, this.d3.tsvParse);
return this.convertXsvToData(tsv, { parse: this.d3.tsvParse, parseRows: this.d3.tsvParseRows });
};
c3_chart_internal_fn.convertJsonToData = function (json, keys) {
var $$ = this,
@ -93,7 +91,7 @@ c3_chart_internal_fn.findValueInJson = function (object, path) {
/**
* Converts the rows to normalized data.
* @param {any[][]} rows The row data
* @return {Object[]}
* @return {Object}
*/
c3_chart_internal_fn.convertRowsToData = (rows) => {
const newRows = [];
@ -109,16 +107,17 @@ c3_chart_internal_fn.convertRowsToData = (rows) => {
}
newRows.push(newRow);
}
return newRows;
return { keys, rows: newRows };
};
/**
* Converts the columns to normalized data.
* @param {any[][]} columns The column data
* @return {Object[]}
* @return {Object}
*/
c3_chart_internal_fn.convertColumnsToData = (columns) => {
const newRows = [];
const keys = [];
for (let i = 0; i < columns.length; i++) {
const key = columns[i][0];
@ -131,16 +130,33 @@ c3_chart_internal_fn.convertColumnsToData = (columns) => {
}
newRows[j - 1][key] = columns[i][j];
}
keys.push(key);
}
return newRows;
return { keys, rows: newRows };
};
/**
* Converts the data format into the target format.
* @param {!Object} data
* @param {!Array} data.keys Ordered list of target IDs.
* @param {!Array} data.rows Rows of data to convert.
* @param {boolean} appendXs True to append to $$.data.xs, False to replace.
* @return {!Array}
*/
c3_chart_internal_fn.convertDataToTargets = function (data, appendXs) {
var $$ = this, config = $$.config,
ids = $$.d3.keys(data[0]).filter($$.isNotX, $$),
xs = $$.d3.keys(data[0]).filter($$.isX, $$),
targets;
var $$ = this, config = $$.config, targets, ids, xs, keys;
// handles format where keys are not orderly provided
if (isArray(data)) {
keys = Object.keys(data[ 0 ]);
} else {
keys = data.keys;
data = data.rows;
}
ids = keys.filter($$.isNotX, $$);
xs = keys.filter($$.isX, $$);
// save x for update data by load when custom x and c3.x API
ids.forEach(function (id) {

Loading…
Cancel
Save