JSON format for Google chart stacked column - javascript

I have data like below:
store 1 Store 2
store_id walk-ins walk-ins
morning 20 25
noon 35 40
night 50 55
There are 20 stores to chart stacking the values of each row.
Google Charts docs tells me the data array looks like this:
var data = google.visualization.arrayToDataTable([
['Stores', 'Store 1', 'Store 2', 'Store 3', 'Store 4', ... ],
['morning', 10, 24, 20, 32, 18, 5, ...],
['noon', 16, 22, 23, 30, 16, 9, ...],
['night', 28, 19, 29, 30, 12, 13, ...],
]);
I am getting the data via MySQL script / server PHP script. What should the JSON look like? The json_encode($data) from MySQL query returns as follows;
[{"store_name":"Store 1","Time":"Morning","count":"17"}, ...]
but the chart does not load and gives me a message "Table has no columns".
I load JSON as follows:
var url = '/url/updatedata.php?var=querytype';
jQuery.getJSON( url, function(Json) {
// Create and populate the data table.
var data = new google.visualization.DataTable(Json);
....
What is the structure of the JSON for a stacked column chart?
Thanks!

I know this is an old post, but in case if any one needs it, here is the format for Jsondata for using google's stacked column from server code.
{
"cols": [
{
"id": "",
"label": "title",
"pattern": "",
"type": "string"
},
{
"id": "",
"label": "A",
"pattern": "",
"type": "number"
},
{
"type": "string",
"role": "annotation",
"p": {
"role": "annotation"
}
},
{
"id": "",
"label": "B",
"pattern": "",
"type": "number"
},
{
"type": "string",
"role": "annotation",
"p": {
"role": "annotation"
}
},
{
"id": "",
"label": "C",
"pattern": "",
"type": "number"
},
{
"type": "string",
"role": "annotation",
"p": {
"role": "annotation"
}
},
{
"id": "",
"label": "D",
"pattern": "",
"type": "number"
},
{
"type": "string",
"role": "annotation",
"p": {
"role": "annotation"
}
}
],
"rows": [
{
"c": [
{
"v": "Categories",
"f": null
},
{
"v": "10",
"f": null
},
{
"v": "10",
"f": null
},
{
"v": "20",
"f": null
},
{
"v": "20",
"f": null
},
{
"v": "30",
"f": null
},
{
"v": "30",
"f": null
},
{
"v": "50",
"f": null
},
{
"v": "50",
"f": null
}
]
}
]
}

It turned out that mapping of your data happened to be a quite complex task.
For example, you have this json data:
var json = [
{"store_name":"Store 1","Time":"Morning","count":"10"},
{"store_name":"Store 1","Time":"Noon","count":"16"},
{"store_name":"Store 1","Time":"Night","count":"28"},
{"store_name":"Store 2","Time":"Morning","count":"24"},
{"store_name":"Store 2","Time":"Noon","count":"22"},
{"store_name":"Store 2","Time":"Night","count":"19"}
];
I use underscore.js for the following code. It can be included like this:
<script type="text/javascript" src="http://underscorejs.org/underscore-min.js"></script>
Mapping code:
var header = _.chain(json).pluck("store_name").sort().uniq(true).value();
header.unshift("Stores");
var rows = _.chain(json)
.groupBy(function(item) { return item.Time; })
.map(function(group, key) {
var result = [key];
_.each(group, function(item) {
result[_.indexOf(header, item.store_name)] = parseInt(item.count);
});
return result;
})
.value();
var jsonData = [header].concat(rows);
// draw chart
var data = google.visualization.arrayToDataTable(jsonData);
chart.draw(data, options);
The final jsonData variable looks so:
[["Stores", "Store 1", "Store 2"],
["Morning", 10, 24],
["Noon", 16, 22],
["Night", 28, 19]]
Here is a link to jsFiddle with your chart.

Related

Read Google Sheet Exported JSON by column name in Apps Script

I'm exploring the use of JSON exported Google Sheets as database for Apps Script.
The fetched url follows the structure:
https://docs.google.com/spreadsheets/d/DOCUMENTID/gviz/tq?tqx=out:json&gid=SHEETID
JSON.json
{
"reqId": "0",
"sig": "000",
"status": "ok",
"table": {
"cols": [
{
"id": "A",
"label": "",
"type": "string"
},
{
"id": "B",
"label": "",
"type": "string"
},
{
"id": "C",
"label": "",
"type": "string"
}
],
"parsedNumHeaders": 0,
"rows": [
{
"c": [
{
"v": "Data 1-1"
},
{
"v": "Data 1-2"
},
{
"v": "Data 1-3"
}
]
},
{
"c": [
{
"v": "Data 2-1"
},
{
"v": "Data 2-2"
},
{
"v": "Data 2-3"
}
]
},
{
"c": [
{
"v": "Emails"
},
{
"v": "Ids"
},
{
"v": "Names"
}
]
}
]
},
"version": "0.6"
}
In this function I'm getting returned an array of values for the C column:
dataArray = [Data 1-3, Data 2-3]
function getRolePermission(databaseUrl) {
let databaseParsed = JSON.parse(UrlFetchApp.fetch(databaseUrl).getContentText().match(/(?<=.*\().*(?=\);)/s)[0]);
let tableLength = Object.keys(databaseParsed.table.rows).length;
let dataArray = [];
for (let i = 0; i < tableLength; i++) {
dataArray.push(databaseParsed.table.rows[i].c[2].v)
}
return dataArray;
}
It works well, but I don't know how to make this function more generic, so I call it with (url, headerName) arguments to get an array with the values of a column. Something like:
function getRolePermission(databaseUrl, headerName) {
// CODE ??
return dataArray;
}
getRolePermission('https://docs.google.com/spreadsheets/d/1lc...', 'Emails')
to get dataArray = [Data 1-3, Data 2-3], so if I change the order of columns I'm still getting the same results.
dataArray.push(databaseParsed.table.rows[i].c[2].v)
You just need to find the index 2 here to make the function more generic. The table.cols should give you the column headers, but it is not doing that because parsedNumHeaders is 0 in your case. To manually set number of headers, use &headers=1. The url should look like:
https://docs.google.com/spreadsheets/d/DOCUMENTID/gviz/tq?tqx=out:json&gid=SHEETID&headers=1
The response then would look like:
{
"reqId": "0",
"sig": "000",
"status": "ok",
"table": {
"cols": [
{
"id": "A",
"label": "Names",
"type": "string"
},
{
"id": "B",
"label": "Ids",
"type": "string"
},
{
"id": "C",
"label": "Emails",
"type": "string"
}
],
"parsedNumHeaders": 1,
"rows": [
{
"c": [
{
"v": "Data 1-1"
},
{
"v": "Data 1-2"
},
{
"v": "Data 1-3"
}
]
},
{
"c": [
{
"v": "Data 2-1"
},
{
"v": "Data 2-2"
},
{
"v": "Data 2-3"
}
]
}
]
},
"version": "0.6"
}
Once you get the headers, you can find the index:
const columnNeeded = databaseParsed.table.cols.findIndex(obj => obj.label === headerName/*"Emails"*/);
Then, use it in your function
dataArray.push(databaseParsed.table.rows[i].c[columnNeeded].v)

How to get value outside click function

I have a div of a map and a div of pie chart on the web page. There are several pins on the map, and I want to refresh the chart div based on different click.
Here is the click function of leaflet.js:
var country = "";
map.on('click', function(e) {
country = "Worldwide";
alert("Set map to worldwide");
});
markerUS.on('click', function(e) {
country = "U.S.";
alert('U.S');
});
in the chart.js (pie chart):
AmCharts.makeChart("chartdiv1", {
"type": "pie",
"angle": 12,
"balloonText": "[[title]]<br><span style='font-size:14px'><b>[[value]]</b> ([[percents]]%)</span>",
"depth3D": 15,
"titleField": "category",
"valueField": "column-1",
"allLabels": [],
"balloon": {},
"legend": {
"enabled": true,
"align": "center",
"markerType": "circle"
},
"titles": [{
"id": "Title-1",
"size": 15,
"text": "Number of Projects distribution of " + country
}],
"dataProvider": [{
"category": "a",
"column-1": 8
},
{
"category": "b",
"column-1": 6
},
{
"category": "c",
"column-1": 2
},
{
"category": "d",
"column-1": "3"
},
{
"category": "e",
"column-1": "4"
},
{
"category": "f",
"column-1": "2"
}
]
});
inside "titles" I make country as variable based on the pin I clicked.
But I got country is undefined and the value country from leaflet.js seems didn't pass to chart.js. Why? How to correct this and realize the function?
You got undefined country because its value only be initialized when user click on map or on a maker. You could try to update your code like this
map.on('click', function(e) {
var country = "Worldwide";
makeChart(country);
});
markerUS.on('click', function(e) {
var country = "U.S.";
makeChart(country);
});
function makeChart(country) {
AmCharts.makeChart("chartdiv1", {
// your chart option ....
});
}
As Trung said you need to call a function that refrech the chart on each click event
var country = "";
map.on('click', function(e) {
country = "Worldwide";
refrechChart(country);
});
markerUS.on('click', function(e) {
country = "U.S.";
refrechChart(country);
});
and then make the refrechChart function
function refrechChart(country) {
AmCharts.makeChart("chartdiv1", {
"type": "pie",
"angle": 12,
"balloonText": "[[title]]<br><span style='font-size:14px'><b>[[value]]</b> ([[percents]]%)</span>",
"depth3D": 15,
"titleField": "category",
"valueField": "column-1",
"allLabels": [],
"balloon": {},
"legend": {
"enabled": true,
"align": "center",
"markerType": "circle"
},
"titles": [{
"id": "Title-1",
"size": 15,
"text": "Number of Projects distribution of " + country
}],
"dataProvider": [{
"category": "a",
"column-1": 8
},
{
"category": "b",
"column-1": 6
},
{
"category": "c",
"column-1": 2
},
{
"category": "d",
"column-1": "3"
},
{
"category": "e",
"column-1": "4"
},
{
"category": "f",
"column-1": "2"
}
]
});
}
you can also pass more parameter to use in your chart like
function refrechChart(country,value1,value2) {
//...
}

Fuzzy search deep array only on certain properties

I have a JSON dataset which could be very large when it returns, with the following structure for each object:
{
"ctr": 57,
"averageECPC": 23,
"cost": 2732.54,
"margin": 66,
"profit": 2495.9,
"property": {
"value": "Izzby",
"uri": "/Terrago/2"
},
"status": {
"content": "<p>Some Content</p>",
"stage": 1
},
"alerts": {
"status": 2
},
"revenue": {
"value": 2573.13,
"compare": 0
},
"children": [{
"ctr": 79,
"averageECPC": 54,
"cost": 3554.78,
"margin": 88,
"profit": 3145.81,
"property": {
"value": "Comvex",
"uri": "/Octocore/4"
},
"status": {
"content": "<p>Some Content</p>",
"stage": 1
},
"alerts": {
"status": 2
},
"revenue": {
"value": 1247.92,
"compare": 0
}
}]
}
Now I want to search all objects in the array and return only objects which include a string of some sort, but I only want to search certain properties.
I basically have another array which contains the keys I want to search, e.g.
const iteratees = ['ctr', 'property.value', 'status.stage']
I have lodash available within the project, but I have no idea where to start.
Any ideas?
You could use filter(), some() and reduce() to do this.
const iteratees = ['ctr', 'property.value', 'status.stage'];
var searchFor = 'lo';
var result = arr.filter(function(o) {
return iteratees.some(function(e) {
var res = e.split('.').reduce(function(a, b) {
if(a) return a[b];
}, o);
if(res) {
if((res).toString().indexOf(searchFor) != -1) return true;
}
})
})
var arr = [{
"ctr": 'lorem',
"averageECPC": 23,
"cost": 2732.54,
"margin": 66,
"profit": 2495.9,
"property": {
"value": "Izzby",
"uri": "/Terrago/2"
},
"status": {
"content": "<p>Some Content</p>",
"stage": 1
},
"alerts": {
"status": 2
},
"revenue": {
"value": 2573.13,
"compare": 0
},
"children": [{
"ctr": 79,
"averageECPC": 54,
"cost": 3554.78,
"margin": 88,
"profit": 3145.81,
"property": {
"value": "Comvex",
"uri": "/Octocore/4"
},
"status": {
"content": "<p>Some Content</p>",
"stage": 1
},
"alerts": {
"status": 2
},
"revenue": {
"value": 1247.92,
"compare": 0
}
}]
}, {
name: 'lorem',
ctr: 12,
property: {
value: 1
},
status: {
stage: 1
}
}, {
name: 'ipsum'
}]
const iteratees = ['ctr', 'property.value', 'status.stage'];
var searchFor = 'lo';
var result = arr.filter(function(o) {
return iteratees.some(function(e) {
var res = e.split('.').reduce(function(a, b) {
if (a) return a[b];
}, o);
if (res) {
if ((res).toString().indexOf(searchFor) != -1) return true;
}
})
})
console.log(result)

display only value fields from the msg.payload in node red

I am working on node red (SNMP).
When I deploy, I have the output below:
[ { "oid": "1.3.6.1.2.1.10.21.1.2.1.1.2.1.26", "type": 2, "value":
104, "tstr": "Integer" }, { "oid": "1.3.6.1.2.1.10.21.1.2.1.1.2.2.27", "type": 2, "value": 104,
"tstr": "Integer" }, { "oid": "1.3.6.1.2.1.10.21.1.2.1.1.2.10.28",
"type": 2, "value": 1, "tstr": "Integer" }, { "oid":
"1.3.6.1.2.1.10.21.1.2.1.1.2.11.29", "type": 2, "value": 1, "tstr":
"Integer" }, { "oid": "1.3.6.1.2.1.10.21.1.2.1.1.2.12.30", "type": 2,
"value": 1, "tstr": "Integer" }, { "oid":
"1.3.6.1.2.1.10.21.1.2.1.1.2.13.31", "type": 2, "value": 1,
"tstr": "Integer" }, { "oid": "1.3.6.1.2.1.10.21.1.2.1.1.2.14.32",
"type": 2, "value": 101, "tstr": "Integer" }, { "oid":
"1.3.6.1.2.1.10.21.1.2.1.1.2.15.38", "type": 2, "value": 1,
"tstr": "Integer" }, { "oid": "1.3.6.1.2.1.10.21.1.2.1.1.2.100.39",
"type": 2, "value": 101, "tstr": "Integer" }, { "oid":
"1.3.6.1.2.1.10.21.1.2.1.1.2.101.40", "type": 2, "value": 101,
"tstr": "I ....
so I want to display all of the values from this output (104, 104, 1, 1 ....)
I am writing this function:
for(var i =0; i<Object.keys(msg.payload).length;i++)
{
msg.payload+=msg.payload[Object.keys(msg.payload)[i]].value;
}
return msg;
but I have an error:
TypeError: Object.keys called on non-object
any idea?
The problem is that your for loop is modifying msg.payload on each iteration - and because it is doing a += it is turning it into a String. That means the second time through the loop, msg.payload is no longer the object it was at the start so the Object.keys call fails.
You should build up your result in a new variable and set msg.payload at the end:
var result = [];
var keys = Object.keys(msg.payload);
for(var i =0; i<keys.length;i++)
{
result.push(msg.payload[keys[i]].value);
}
// At this point, result is an array of the values you want
// You can either return it directly with:
// msg.payload = result;
// or, if you want a string representation of the values as a
// comma-separated list:
// msg.payload = result.join(", ");
return msg;

Create a map out of a grouped array objects

I'm using Underscore.js to map a new object array out of an existing object array but cannot really get the desired results.
Essentially I have an object array like:
[
{
"total": 5.21,
"number": 3,
"a": "Paid",
"y": 2015,
"m": 1,
"d": "2015-01-17T23:58:34.115Z"
},
{
"total": 374.65,
"number": 3,
"a": "Scheduled",
"y": 2015,
"m": 1,
"d": "2015-01-18T02:16:03.503Z"
},
{
"total": 310.84,
"number": 1,
"a": "Paid",
"y": 2015,
"m": 1,
"d": "2015-01-17T23:58:34.115Z"
},
{
"total": 284.41,
"number": 3,
"a": "Scheduled",
"y": 2015,
"m": 1,
"d": "2015-01-18T02:16:03.503Z"
}
]
which I would like to map into something like:
[
{
"key": "Paid",
"values": [
[
"2015-01-17T23:58:34.115Z",
5.21
],
[
"2015-01-17T23:58:34.115Z",
310.84
]
]
},
{
"key": "Scheduled",
"values": [
[
"2015-01-18T02:16:03.503Z",
374.65
],
[
"2015-01-18T02:16:03.503Z",
284.41
]
]
}
]
I've tried using the ._map method returns a map like this (JSFiddle):
var mapped_bill = _.map(bill, function(item) {
return {"key": item.a, "values": [item.d, item.total]}
});
console.log(JSON.stringify(mapped_bill));
/* returns:
[
{
"key": "Paid",
"values": [
"2015-01-17T23:58:34.115Z",
5.21
]
},
{
"key": "Scheduled",
"values": [
"2015-01-18T02:16:03.503Z",
374.65
]
},
{
"key": "Paid",
"values": [
"2015-01-17T23:58:34.115Z",
310.84
]
},
{
"key": "Scheduled",
"values": [
"2015-01-18T02:16:03.503Z",
284.41
]
}
]
*/
How do I group the resulting map above so that I can achieve the desired map?
You can use two _.map methods with _.groupBy:
var result = _.map(_.groupBy(data, 'a'), function(el, key) {
return {
key: key,
values: _.map(el, function(item) {
return [item.d, item.total];
})
};
});
Check the demo below.
var data = [
{
"total": 5.21,
"number": 3,
"a": "Paid",
"y": 2015,
"m": 1,
"d": "2015-01-17T23:58:34.115Z"
},
{
"total": 374.65,
"number": 3,
"a": "Scheduled",
"y": 2015,
"m": 1,
"d": "2015-01-18T02:16:03.503Z"
},
{
"total": 310.84,
"number": 1,
"a": "Paid",
"y": 2015,
"m": 1,
"d": "2015-01-17T23:58:34.115Z"
},
{
"total": 284.41,
"number": 3,
"a": "Scheduled",
"y": 2015,
"m": 1,
"d": "2015-01-18T02:16:03.503Z"
}
];
var result = _.map(_.groupBy(data, 'a'), function(el, key) {
return {
key: key,
values: _.map(el, function(item) {
return [item.d, item.total];
})
};
});
pre.innerHTML = JSON.stringify(result, null, 4);
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.7.0/underscore-min.js"></script>
<pre id="pre"></pre>
Going by your history here then it could be assumed this is actually a mongodb question even though you are just looking at the resulting JavaScript object in the question as presented.
So with the structure you mention actually being the members of a MongoDB collection then the answer to produce the the required output would be:
db.mapped.aggregate([
{ "$group": {
"_id": "$a",
"values": {
"$push": {
"$map": {
"input": { "$literal": ["A","B"] },
"as": "l",
"in": {
"$cond": [
{ "$eq": [ "$$l", "A" ] },
"$d",
"$total"
]
}
}
}
}
}}
])
So the $map operator there processes our "two element array template" provided in the $literal operator section, and "transposes" the values via the $cond "ternary" to either produce the element from "$d" where the first "A" element is matched or the element "$total" where the element is not "A" but therefore "B" as the only other logical choice.
Results in mapping an array that has the first elementof the first match and the second element as the other expected value. These can then be provided to $push, to create and "array of arrays" as requested.
Which produces from your source as a collection:
{
"_id" : "Scheduled",
"values" : [
[
"2015-01-18T02:16:03.503Z",
374.65
],
[
"2015-01-18T02:16:03.503Z",
284.41
]
]
},
{
"_id" : "Paid",
"values" : [
[
"2015-01-17T23:58:34.115Z",
5.21
],
[
"2015-01-17T23:58:34.115Z",
310.84
]
]
}
So you didn't need this post processing in JavaScript as you thought you did. Using the operators that are appropriate to match your conditions on the server side $group is all you need.

Categories

Resources