Sorry for the terrible title, I don't know how to properly describe this.
I have a set of table headers that are hierarchical, and I need to transform them from a multi-column tree structure, to a single column indented structure.
The Tree like structure looks like this (Sorry it's photos, I can't render it in MD):
And the version I would like to render looks like this:
In the first photo, the yellow cells are rollup cells. They don't actually have any information, they just act as placeholders for alignment.
The data structure for the first table headers looks like essentially like this:
{
"headers": [
[
{
"label": "A",
"span": "9"
}
],
[
{
"label": "B",
"span": "9"
}
],
[
{
"label": "C",
"span": "1"
},
{
"label": "D",
"span": "4"
},
{
"label": "E",
"span": "1"
},
{
"label": "F",
"span": "3"
}
],
[
{
"rollup": true,
"span": "1"
},
{
"label": "G",
"span": "2"
},
{
"label": "H",
"span": "2"
},
{
"rollup": true,
"span": "1"
},
{
"label": "I",
"span": "1"
},
{
"label": "J",
"span": "1"
},
{
"label": "K",
"span": "1"
}
],
[
{
"rollup": true,
"span": "1"
},
{
"label": "L",
"span": "1"
},
{
"label": "M",
"span": "1"
},
{
"label": "N",
"span": "1"
},
{
"label": "O",
"span": "1"
},
{
"rollup": true,
"span": "1"
},
{
"rollup": true,
"span": "1"
},
{
"rollup": true,
"span": "1"
},
{
"rollup": true,
"span": "1"
},
],
]
}
And the data for the actual values in the table looks like this (it's possible to have many columns, this the nested arrays, furthermore it's possible to have empty values in which case there will be a flag that indicated it's an empty cell like "empty":true):
const rows = {
"data": [
[
{
"value": 100
"empty": false // Could be used to indicate a cell is empty
}
],
[
{
"value": 200
}
],
[
{
"value": 300
}
],
[
{
"value": 400
}
],
[
{
"value": 500
}
],
[
{
"value": 600
}
],
[
{
"value": 700
}
],
[
{
"value": 800
}
],
[
{
"value": 900
}
],
]
}
I need to render the second header style, using the data above. This doesn't feel like it should be a very difficult problem, however I can't seem to solve it.
edit:
To render the data in the first table, we convert the initial data structure essentially into a 9x5 matrix, where the arrays are padded with null accordingly.
const yHeaders = {
"headers": [
[
{
"label": "A",
"span": "9",
},
null,
null,
null,
null,
null,
null,
null,
null,
],
[
{
"label": "B",
"span": "9"
},
null,
null,
null,
null,
null,
null,
null,
null,
],
[
{
"label": "C",
"span": "1",
},
{
"label": "D",
"span": "4",
},
null,
null,
null,
{
"label": "E",
"span": "1",
},
{
"label": "F",
"span": "3",
},
null,
null,
],
[
{
"rollup": true,
"span": "1",
},
{
"label": "G",
"span": "2",
},
null,
{
"label": "H",
"span": "2",
},
null,
{
"rollup": true,
"span": "1",
},
{
"label": "I",
"span": "1",
},
{
"label": "J",
"span": "1",
},
{
"label": "K",
"span": "1",
}
],
[
{
"rollup": true,
"span": "1",
},
{
"label": "L",
"span": "1",
},
{
"label": "M",
"span": "1",
},
{
"label": "N",
"span": "1",
},
{
"label": "O",
"span": "1",
},
{
"rollup": true,
"span": "1",
},
{
"rollup": true,
"span": "1",
},
{
"rollup": true,
"span": "1",
},
{
"rollup": true,
"span": "1",
},
],
]
}
To render the current table, we use something like this:
const renderBodyRow = (row: any, key: number, rows: any) => {
return (
<tr key={key}>
{yHeaders.headers.map((yHeaderColumn: any[], rowColumnHeaderKey: number) => {
// For many of these, yHeaderColumn[key] will be null, in which case the previous
// row will simply span into the new one
return (
yHeaderColumn.length > key &&
yHeaderColumn[key] && (
<th
key={rowColumnHeaderKey}
rowSpan={+get(yHeaderColumn[key], "span", 1)}
>
{get(yHeaderColumn[key], "label")}
</th>
)
);
})}
{row.map((cell: any, cellIndex: number) => {
return <td key={cellIndex}>{get(cell, "value", "")}</td>;
})}
</tr>
);
};
return (
<table id={tableId}>
<thead>...</thead>
<tbody >
{rows.data.map(renderBodyRow)}
</tbody>
</table>
)
You could take a virtual target by using span and the intermediate indices to get a tree structure and render a flat array with indented label and value, denoted here with data[0], which should be replaced by the real values.
const
headers = [[{ label: "A", span: "9" }], [{ label: "B", span: "9" }], [{ label: "C", span: "1" }, { label: "D", span: "4" }, { label: "E", span: "1" }, { label: "F", span: "3" }], [{ rollup: true, span: "1" }, { label: "G", span: "2" }, { label: "H", span: "2" }, { rollup: true, span: "1" }, { label: "I", span: "1" }, { label: "J", span: "1" }, { label: "K", span: "1" }], [{ rollup: true, span: "1" }, { label: "L", span: "1" }, { label: "M", span: "1" }, { label: "N", span: "1" }, { label: "O", span: "1" }, { rollup: true, span: "1" }, { rollup: true, span: "1" }, { rollup: true, span: "1" }, { rollup: true, span: "1" }]],
iter = (array, index = { i: 0 }) => array.flatMap(({ label, children }) => [
[label, children.length ? '' : `data[${index.i++}]`],
...iter(children, index)
]),
temp = [];
headers.reduce((r, a, i) => {
const parents = {};
let pos = 0;
a.forEach(({ label, span, rollup }) => {
span *= 1;
label = ' '.repeat(i) + label;
if (!rollup) {
r[pos].push({ label, children: parents[pos] = [] });
for (let i = 1; i < span; i++) parents[pos + i] = parents[pos];
}
pos += span;
});
return parents;
}, { 0: temp });
console.log(iter(temp));
console.log(temp);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
Currently i have below Array of Objects
const dataClass = [
{
"id": 101,
"class": [
{
"type": "A",
"value": "A-class"
},
{
"type": "B",
"value": "B-class"
},
{
"type": "C",
"value": "C-class"
}
],
"rank": 1
},
{
"id": 102,
"class": [
{
"type": "D",
"value": "D-class"
},
{
"type": "E",
"value": "E-class"
},
{
"type": "F",
"value": "F-class"
}
],
"rank": 2
},
{
"id": 103,
"class": [
{
"type": "G",
"value": "G-class"
},
{
"type": "H",
"value": "H-class"
},
{
"type": "I",
"value": "I-class"
}
],
"rank": 3
}
];
i need to get dataClass object using all value inside the class object, let say i want to get the second object, so i have to search/input "type": "D", "type": "E", and "type": "F".
return array object/object i expect:
[{
"id": 102,
"class": [
{
"type": "D",
"value": "D-class"
},
{
"type": "E",
"value": "E-class"
},
{
"type": "F",
"value": "F-class"
}
],
"rank": 2
}]
I don't find any solution so far, Thanks for any help.
I added one more object with types D, E, F at rank 4
If you want to return all objects that match your filtration, check result1
and if you just wanna return the first object that matches, check result2
const dataClass = [
{
"id": 101,
"class": [
{
"type": "A",
"value": "A-class"
},
{
"type": "B",
"value": "B-class"
},
{
"type": "C",
"value": "C-class"
}
],
"rank": 1
},
{
"id": 102,
"class": [
{
"type": "D",
"value": "D-class"
},
{
"type": "E",
"value": "E-class"
},
{
"type": "F",
"value": "F-class"
}
],
"rank": 2
},
{
"id": 103,
"class": [
{
"type": "G",
"value": "G-class"
},
{
"type": "H",
"value": "H-class"
},
{
"type": "I",
"value": "I-class"
}
],
"rank": 3
},
{
"id": 104,
"class": [
{
"type": "D",
"value": "D-class"
},
{
"type": "E",
"value": "E-class"
},
{
"type": "F",
"value": "F-class"
}
],
"rank": 4
}
];
const expectedValues = ['D', 'E', 'F'];
//use this if you wanna return all objects that match expectedValues
const result1 = dataClass.filter(el => el.class.every(obj => expectedValues.includes(obj.type)));
console.log('all matched Objects => ', result1);
//use this if you wanna return the first object that match expectedValues
const result2 = dataClass.find(el => el.class.every(obj => expectedValues.includes(obj.type)));
console.log('first matched object => ',result2);
Hope this will help,
const dataClass = [
{
"id": 101,
"class": [
{
"type": "A",
"value": "A-class"
},
{
"type": "B",
"value": "B-class"
},
{
"type": "C",
"value": "C-class"
}
],
"rank": 1
},
{
"id": 102,
"class": [
{
"type": "D",
"value": "D-class"
},
{
"type": "E",
"value": "E-class"
},
{
"type": "F",
"value": "F-class"
}
],
"rank": 2
},
{
"id": 103,
"class": [
{
"type": "G",
"value": "G-class"
},
{
"type": "H",
"value": "H-class"
},
{
"type": "I",
"value": "I-class"
}
],
"rank": 3
}
];
const resultArr = [];
for (const ch_arr of dataClass){
for (const class_arr of ch_arr["class"]){
if(["D","E","F"].includes(class_arr["type"])){
resultArr.push(ch_arr);
break;
}
};
};
// resultArr is the expected array
You need find the object inside of class Array so i think using find method is the more readable way to solved it
function findClassByType(value: string) {
return [dataClass.find((obj) => obj.class.find(({ type }) => type.toLocaleLowerCase() === value.toLocaleLowerCase()))];
}
console.log(findClassByType('a'))
I added the toLocaleLowerCase to avoid case sensitive.
I have two JSON strings as shown below:
source = [
{
"name": "test1",
"values": ["User Support"],
"enabled": false
},
{
"name": "test2",
"values": ["M"],
"enabled": true
},
{
"name": "test3",
"values": ["CA"],
"enabled": false
}
]
target = [{
"name": "test1",
"values": [{
"value": "User Support",
"selected": false
},
{
"value": "Engineering",
"selected": false
},
{
"value": "Implementation",
"selected": false
}
],
"enabled": false
},
{
"name": "test2",
"values": [{
"value": "M",
"selected": false
},
{
"value": "F",
"selected": false
}
],
"notEnabled": false
},
{
"name": "test3",
"values": [{
"value": "CA",
"selected": false
},
{
"value": "EN",
"selected": false
}
],
"enabled": false
}
]
I want to merge both these JSON strings into target and the resultant should look like:
target = [{
"name": "test1",
"values": [{
"value": "User Support",
"selected": true
},
{
"value": "Engineering",
"selected": false
},
{
"value": "Implementation",
"selected": false
}
],
"enabled": false
},
{
"name": "test2",
"values": [{
"value": "M",
"selected": true
},
{
"value": "F",
"selected": false
}
],
"enabled": true
},
{
"name": "test3",
"values": [{
"value": "CA",
"selected": true
},
{
"value": "EN",
"selected": false
}
],
"enabled": false
}
]
So, what I am trying to do is search in target string for name as test1, test2.... and then set the selected field as true if the value is found in source JSON string. Same is the case for enabled field.
First thing that comes to my mind is to use nested for each loops and check for the keys.
Is there any other better way to do this in Javascript?
Note that there could be other keys present inside target string, but we don't bother about them unless they are present in source string.
If you don't mind lodash:
const _ = require('lodash');
const sourceJSON = '[{"name":"test1","values":["User Support"],"enabled":false},{"name":"test2","values":["M"],"enabled":true},{"name":"test3","values":["CA"],"enabled":false}]';
const targetJSON = '[{"name":"test1","values":[{"value":"User Support","selected":false}, {"value":"Engineering","selected":false},{"value":"Implementation","selected":false}],"enabled":false},{"name":"test2","values":[{"value":"M","selected":false}, {"value":"F","selected":false} ],"notEnabled":false},{ "name":"test3","values": [{"value":"CA","selected":false},{"value":"EN","selected":false}],"enabled":false}]';
const source = JSON.parse(sourceJSON);
const target = JSON.parse(targetJSON);
const sourceNormalized = source.map((obj) => (
{ ...obj, values: [{value: obj.values[0], selected: true}] }
));
const merged = _.defaultsDeep(sourceNormalized, target);
console.dir(merged, {depth: null});
// [ { name: 'test1',
// values: [
// { value: 'User Support', selected: true },
// { value: 'Engineering', selected: false },
// { value: 'Implementation', selected: false }
// ],
// enabled: false
// },
// { name: 'test2',
// values: [
// { value: 'M', selected: true },
// { value: 'F', selected: false }
// ],
// enabled: true,
// notEnabled: false
// },
// { name: 'test3',
// values: [
// { value: 'CA', selected: true },
// { value: 'EN', selected: false }
// ],
// enabled: false} ]
result = JSON.stringify(merged);
The previous answer using lodash is a very good approach. In case you are in a situation such as myself (stuck working on a code-base where packages, including lodash, are difficult to get approved-for-use), here is an approach using Vanilla-javascript (+ JSON.stringify):
const delta = [{
"name": "test1",
"values": ["User Support"],
"enabled": false
},
{
"name": "test2",
"values": ["M"],
"enabled": true
},
{
"name": "test3",
"values": ["CA"],
"enabled": false
}
];
const orig = [{
"name": "test1",
"values": [{
"value": "User Support",
"selected": false
},
{
"value": "Engineering",
"selected": false
},
{
"value": "Implementation",
"selected": false
}
],
"enabled": false
},
{
"name": "test2",
"values": [{
"value": "M",
"selected": false
},
{
"value": "F",
"selected": false
}
],
"notEnabled": false
},
{
"name": "test3",
"values": [{
"value": "CA",
"selected": false
},
{
"value": "EN",
"selected": false
}
],
"enabled": false
}
];
const getTargetJSON = (delta, orig, debug = true) => { // method signature
const deltaMapper = delta.reduce((f, i) => ({
...f,
[i.name]: {
values: [...i.values],
enabled: i.enabled
}
}), {});
const target = orig.map(itm => ({
...itm,
values: itm.values.map(it2 => ({
...it2,
selected: deltaMapper[itm.name].values.includes(it2.value) || false
})),
enabled: deltaMapper[itm.name].enabled || false
}));
debug && console.log('Merged Target:\n', target);
return JSON.stringify(target);
};
getTargetJSON(delta, orig); // method call
Approach / Explanation:
We breakdown the problem into smaller ones.
First, generate a map using the source (named 'delta' in
code-snippet)
Next, iterate through the target (named 'orig') and utilize the
map (created above) to determine whether value is 'selected' and
item is 'enabled'.
I have a Json data that I need adjust before sending it to my component. My Json is as below. I need to identify the missing fields and move the below ones up.
[{
"Id": "a",
"ColumnLocation": "0",
"RowLocation": "0"
}, {
"Id": "b",
"ColumnLocation": "0",
"RowLocation": "1"
},
{
"Id": "4",
"ColumnLocation": "0",
"RowLocation": "3"
},
{
"Id": "c",
"ColumnLocation": "1",
"RowLocation": "0"
}, {
"Id": "d",
"ColumnLocation": "1",
"RowLocation": "2"
}, {
"Id": "e",
"ColumnLocation": "2",
"RowLocation": "0"
},
{
"Id": "e",
"ColumnLocation": "2",
"RowLocation": "2"
}]
My required Json is:
[{
"Id": "a",
"ColumnLocation": "0",
"RowLocation": "0"
}, {
"Id": "b",
"ColumnLocation": "0",
"RowLocation": "1"
},
{
"Id": "4",
"ColumnLocation": "0",
"RowLocation": "2"
},
{
"Id": "c",
"ColumnLocation": "1",
"RowLocation": "0"
}, {
"Id": "d",
"ColumnLocation": "1",
"RowLocation": "1"
}, {
"Id": "e",
"ColumnLocation": "2",
"RowLocation": "0"
},
{
"Id": "e",
"ColumnLocation": "2",
"RowLocation": "1"
}]
Here after (0,0), (0,1), property (0,2) is missing so I need to move it up and make it (0,2).. Same way after(1,0), property(1,1) is missing so it has to be (1,1).
I tried writing a custom function for this, but couldn't able to achieve it, so thought any map function that fits this scenario
I am getting gadgets information from the API. In some cases, some gadgets might be missing, so I need to pull there location up and draw the gadgets.
this.userService.getGadgets(id).subscribe(gadgets => { this.res = gadgets.map(function (v) { return v.ColumnLocation; });
// required logic ************/
for (let gadget of gadgets) {
this.dashboardsText = "";
switch (gadget.Name) {
You could store the last column and row, then check if it is not the same column, then reset row counter.
Then check if RowLocation is equal to row counter and if not set the new value.
Finally increment row counter.
var array = [{ Id: "a", ColumnLocation: "0", RowLocation: "0" }, { Id: "b", ColumnLocation: "0", RowLocation: "1" }, { Id: "4", ColumnLocation: "0", RowLocation: "3" }, { Id: "c", ColumnLocation: "1", RowLocation: "0" }, { Id: "d", ColumnLocation: "1", RowLocation: "2" }, { Id: "e", ColumnLocation: "2", RowLocation: "0" }, { Id: "e", ColumnLocation: "2", RowLocation: "2" }];
array.forEach(function (col, row) {
return function (o) {
if (col !== o.ColumnLocation) {
col = o.ColumnLocation;
row = 0;
}
if (+o.RowLocation !== row) {
o.RowLocation = row.toString();
}
row++;
}
}());
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You could use global variables instead if the closure, maybe this works for you.
var array = [{ Id: "a", ColumnLocation: "0", RowLocation: "0" }, { Id: "b", ColumnLocation: "0", RowLocation: "1" }, { Id: "4", ColumnLocation: "0", RowLocation: "3" }, { Id: "c", ColumnLocation: "1", RowLocation: "0" }, { Id: "d", ColumnLocation: "1", RowLocation: "2" }, { Id: "e", ColumnLocation: "2", RowLocation: "0" }, { Id: "e", ColumnLocation: "2", RowLocation: "2" }],
col,
row;
array.forEach(function (o) {
if (col !== o.ColumnLocation) {
col = o.ColumnLocation;
row = 0;
}
if (+o.RowLocation !== row) {
o.RowLocation = row.toString();
}
row++;
});
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Array.prototype.move = function (old_index, new_index) {
if (new_index >= this.length) {
var k = new_index - this.length;
while ((k--) + 1) {
this.push(undefined);
}
}
this.splice(new_index, 0, this.splice(old_index, 1)[0]);
};
var array, i;
array = [{"Id": "a","ColumnLocation": "0","RowLocation": "0"}, {
"Id": "b","ColumnLocation": "0", "RowLocation": "1"}]
i = array.length;
while (i--) {
if (!array[i].ColumnLocation) {
array.move(i, i+1);
}
Was wondering if anyone knows of a way to use lodash, or vanilla JS to achieve this small problem?
I have this starting object:
{
"1": {
"null": {
"2": {
"3": {
"6": {
"7": "c"
},
"null": {
"null": {
"5": "b"
}
}
}
}
}
},
"8": {
"10": "e",
"null": {
"9": "d"
}
}
}
Each level (horizontally) means something. So level 1 is of type A, level 2 is of type B, 3 of type A, 4 of type B and so forth. So it alternates.
Is there a nice and simply way to "collapse" this object to look something like this:
[
{
"type": "A",
"label": "1",
"children": [
{
"type": "A",
"label": "2",
"children": [
{
"type": "B",
"label": "3",
"children": [
{
"type": "A",
"label": "6",
"children": [
{
"type": "A",
"label": "7",
"value": "c"
}
]
},
{
"type": "A",
"label": "8",
"children": [
{
"type": "A",
"label": "5",
"value": "b"
}
]
}
]
}
]
}
]
},
{
"type": "A",
"label": "8",
"children": [
{
"type": "B",
"label": "10",
"value": "e"
},
{
"type": "A",
"label": "9",
"value": "d"
}
]
}
]
In essence annotating each level with what type it is, and nesting its children.
Here is the code
function transformObj(obj, level) {
level = level || 1;
var result = _(obj).transform(function(result, value, key) {
var obj = {
type: (level % 2 === 0) ? 'B' : 'A',
label: key
};
if (key === 'null') {
result.push(transformObj(value, level+1));
} else {
if (_.isObject(value)) {
obj.children = transformObj(value, level+1);
} else {
obj.value = value;
}
result.push(obj);
}
}, [])
.flatten()
.value();
return result;
}
Here is the output
[
{
"type": "A",
"label": "1",
"children": [
{
"type": "A",
"label": "2",
"children": [
{
"type": "B",
"label": "3",
"children": [
{
"type": "A",
"label": "6",
"children": [
{
"type": "B",
"label": "7",
"value": "c"
}
]
},
{
"type": "A",
"label": "5",
"value": "b"
}
]
}
]
}
]
},
{
"type": "A",
"label": "8",
"children": [
{
"type": "B",
"label": "10",
"value": "e"
},
{
"type": "A",
"label": "9",
"value": "d"
}
]
}
]
This should do the trick:
var source = {
"1": {
"null": {
"2": {
"3": {
"6": {
"7": "c"
},
"null": {
"null": {
"5": "b"
}
}
}
}
}
},
"8": {
"10": "e",
"null": {
"9": "d"
}
}
};
function collapse(obj, parent, level){
var result = parent || [];
level = level || 0;
for(prop in obj){
var item = obj[prop];
var build = {
type : level % 2 ? "B" : "A",
label : prop
//, level : level
}
if(typeof item == 'object'){
build.children = [];
collapse(item, build.children, level + 1);
} else {
build.value = item;
}
result.push(build);
}
return result;
}
var output = collapse(source);
var result = JSON.stringify(output, null, ' ');
console.log(result);
var elem = document.getElementById("result");
elem.innerHTML = result;
<pre id="result"></pre>
function doIt(data){
return _.chain(data)
.transform(function(result, value, key){
if(key !== 'null'){
var type = _.parseInt(key) % 2 === 1 ? 'A' : 'B';
if(_.isObject(value) && !_.includes(_.keys(value), 'prop1')){
result.push({
type: type,
label: key,
children: doIt(value)
});
} else {
result.push({
type: type,
label: key,
value: value
});
}
} else {
if(_.isObject(value)){
result.push(doIt(value));
}
}
}, [])
.flatten()
.value();
}
var result = doIt(data);
result is
[
{
"type": "A",
"label": "1",
"children": [
{
"type": "B",
"label": "2",
"children": [
{
"type": "A",
"label": "3",
"children": [
{
"type": "B",
"label": "6",
"children": [
{
"type": "A",
"label": "7",
"value": "c"
}
]
},
{
"type": "A",
"label": "5",
"value": "b"
}
]
}
]
}
]
},
{
"type": "B",
"label": "8",
"children": [
{
"type": "B",
"label": "10",
"value": "e"
},
{
"type": "A",
"label": "9",
"value": "d"
}
]
}
]
Using MongoDB / Node consider the following payload:
var myObj = {
"date" : "1-23-45",
"one" : [
{
"a" : 8
},
{
"b" : 1
},
{
"c" : 9
},
{
"d" : 10
},
{
"e" : 12424
},
{
"f" : 11
},
{
"g" : 7
}
],
"two" : [
{
"h" : 6
},
{
"i" : 10
}
]
},
{
"date" : "1-24-45",
"one" : [
{
"a" : 8
},
{
"b" : 1
},
{
"c" : 9
},
{
"d" : 10
},
{
"e" : 12424
},
{
"f" : 11
},
{
"g" : 7
}
],
"two" : [
{
"h" : 6
},
{
"i" : 10
}
]
}
I am using Google Charts API and I would like to plot these points to a line graph. (see snippet)
<html>
<head>
<script type="text/javascript" src="https://www.google.com/jsapi?autoload={
'modules':[{
'name':'visualization',
'version':'1',
'packages':['corechart']
}]
}"></script>
<script type="text/javascript">
google.setOnLoadCallback(drawChart);
var myObj = {
"cols": [{
"id": "",
"label": "year",
"type": "string"
}, {
"id": "",
"label": "sales",
"type": "number"
}, {
"id": "",
"label": "expenses",
"type": "number"
}],
"rows": [{
"c": [{
"v": "2001"
}, {
"v": 3
}, {
"v": 5
}]
}, {
"c": [{
"v": "2002"
}, {
"v": 5
}, {
"v": 10
}]
}, {
"c": [{
"v": "2003"
}, {
"v": 6
}, {
"v": 4
}]
}, {
"c": [{
"v": "2004"
}, {
"v": 8
}, {
"v": 32
}]
}, {
"c": [{
"v": "2005"
}, {
"v": 3
}, {
"v": 56
}]
}]
}
function drawChart() {
var data = new google.visualization.DataTable(myObj);
var options = {
title: 'My Chart',
curveType: 'function',
legend: {
position: 'right'
}
};
var chart = new google.visualization.LineChart(document.getElementById('curve_chart'));
chart.draw(data, options);
}
</script>
</head>
<body>
<div id="curve_chart" style="width: 100%; height: 500px"></div>
</body>
</html>
Using the JSON I am provided, what would be the most effective way to massage the data into the format accepted by Google Charts API? I have looked into D3 but it seemed to have a higher learning curve, would that be the most recommended route? Would it be better to query the dataset differently / aggregate the result?
Help is much appreciated, as this has been a 2 day long venture!
Update --
TL;DR
I need a script that goes from Format #1 => Format #2, no matter how big the payload is.
Format #1 - myObj
Format #2 -
var myObj = {
"cols": [{
"label": "Date",
"type": "string"
}, {
"label": "a",
"type": "number"
}, {
"label": "b",
"type": "number"
}, {
"label": "c",
"type": "number"
}, {
"label": "d",
"type": "number"
}, {
"label": "e",
"type": "number"
}, {
"label": "f",
"type": "number"
}, {
"label": "g",
"type": "number"
}, {
"label": "h",
"type": "number"
}, {
"label": "i",
"type": "number"
}
],
"rows": [{
"c": [{
"v": "day1"
}, {
"v": 300
}, {
"v": -500
}, {
"v": 23
}, {
"v": 120
}, {
"v": 150
}, {
"v": 1210
}, {
"v": 160
}, {
"v": 180
}, {
"v": 190
}]
}, {
"c": [{
"v": "day2"
}, {
"v": 1300
}, {
"v": -5200
}, {
"v": 253
}, {
"v": 6120
}, {
"v": 1350
}, {
"v": 110
}, {
"v": 2160
}, {
"v": 1180
}, {
"v": 1190
}]
}
]
}
Looking at your data and how it needs to be formatted, something like the below would work. You will need to loop over each object to get the cols and then map over each array to get the rows.
var dataObj = {
"cols": [],
"rows": []
};
for(property in myObj) {
if(typeof myObj[property] === 'string') {
dataObj.cols.push({
"label": property,
"type": "string"
});
} else {
dataObj.rows.push({
"c": []
});
dataObj.rows[dataObj.rows.length - 1]["c"].push({
"date": "day" + dataObj.rows.length // The day: 1, 2, 3, etc.
});
myObj[property].map(function(object) {
for(prop in object) {
dataObj.cols.push({
"label": prop,
"type": "number"
});
dataObj.rows[dataObj.rows.length - 1]["c"].push({
"v": object[prop]
});
}
});
}
}
JSFiddle with Google Chart example (Open Console to see formatted data)