Google visualization DataTable: calculate totals - javascript

I'm searching a way to add a row of totals to a simple DataTable. This is my code:
// Create and populate the data table.
var dt = new google.visualization.DataTable();
dt.addColumn('string', 'Name');
dt.addColumn('number', 'Height');
dt.addRows([
['Tong Ning mu', 174],
['Huang Ang fa', 523],
['Teng nu', 86]
]);
var myTotal;
/* calculate total sum of column Height */
dt.addRow(['TOTAL', myTotal]);
// Create and draw the visualization.
visualization = new google.visualization.Table(document.getElementById('table'));
visualization.draw(dt, null);
How to calculate myTotal from dt DataTable?
Is it possible to make the last row(Totals) bold?
Is there any more elegant way to add totals to a table?

create the following function:
function getSum(data, column) {
var total = 0;
for (i = 0; i < data.getNumberOfRows(); i++)
total = total + data.getValue(i, column);
return total;
}
call it with your created google datatable, and column array index. In your case:
var total = getSum(dt,1);
Then, you can add that total into a new raw or what ever you want to do with it.

As #Danny's reply is the same solution as I would say for suming the column, I won't write it.
To set a cell property you dataTable.setProperty(columnIndex,rowIndex,'style','font-weight:bold;');
so in your case you'd write
dt.setProperty(0, 3, 'style','font-weight:bold;');
dt.setProperty(1, 3, 'style','font-weight:bold;');
Say you provide a bit bigger dataTable, it might be inconvenient to type this for all cells, you could use a for loop like (to make the whole last row bold):
var lastRow = dt.getNumberOfRows()-1
for (i=0, n=dt.getNumberOfColumns(); i < n; i++) {
dt.setProperty(i, lastRow, 'style', 'font-weight:bold;');
}
Sadly you're unable to set properties to a whole row/column (see the documentation about custom properties regarding this).

Related

How do I input an specific cell for every i in a loop on google script?

One of my projects is making a sales spreadsheet.
The sales spreadsheet contains the names of the products and their prices are in the documentation, the challenge is getting the prices to automatically show up on the cell right next to the product name in the spreadsheet.
Here's what I did:
function Autoprice() {
var sales = SpreadsheetApp.getActive().getSheetByName('Sales')
var salesrow = sales.getRange('D2:D'+sales.getLastRow())
var productnames = salesrow.getValues()
size = productnames.length+1
for (var i = 0; i< size; i++){
if (productnames[i+1]=='Diary')
{
sales.getRange('F'+i).setValue(31.90)
}
And I just input all the prices manually.
The thing is, google script does not read the sales.getRange('F'+1) as I thought it would, and I can't find the correct way to read that for every item in 'DI' cell, i want to put a price on 'FI' cell.
Try using this script, I modified a couple of lines in the sample you shared and added comments next to it to explain.
function Autoprice() {
var sales = SpreadsheetApp.getActive().getSheetByName('Sales')
var salesrow = sales.getRange('D2:D'+sales.getLastRow())
var productnames = salesrow.getValues()
size = productnames.length+1
for (var i = 0; i< size; i++){
if (productnames[i]=='Diary') //If you do productnames[i+1], you're not starting from the beginning of the range, basically you're starting from D3 instead of D2
{
sales.getRange(i+2,6).setValue(31.90) //You can try getRange(row, column) instead
}
}
}
Reference:
getRange(row, column)
You are trying to loop through a 2-dimensionall array (not technically... but each element is a single array).
So to see D2's value you would need productnames[0][0]
However, you can easily fix this using the flat() function. Modify one line of code below:
var productnames = salesrow.getValues().flat();
Also consider learning to use the debugger. If you step through your code, this is easy to see.

Looping through column untill empty row, looking up each value in different SS and return row position

I'm trying to accomplish somewhat of a database table editor that edits rows based on a Trans ID.
I've accomplished vlookups and return values with a vlookup similar script I have, but this time I need to return position and not value.
I need help on how to set this script flow up. I've drawn and wrote everything out on a picture, it was a lot easier to write out that way.
This is what I'm starting with and need some suggestions.
I want to return row position instead of value and repeat until hit first empty row as seen in example picture I attached.
function vlookup(sheet, column, index, value) {
var sheet = sheet
var lastRow= sheet.getLastRow();
var data=sheet.getRange(1,column,lastRow,column+index).getValues();
for(i=0;i<data.length;++i){
if (data[i][0]==value){
return data[i][index];
}
}
}
The row number is just the index of the array + 1 as arrays start at 0.
return i + 1;
Heres the script on how I accomplished the task.
function doit(){
var lastrow = gv.glr(gv.invhelper,"A") // global function getting last row of target/loop column
for (var i = 4; i < lastrow+1; i++){ //loop statement
// actions to repeat
var id = gv.invhelper.getRange("A"+i).getValue(); //get id from row
var duradj = gv.invhelper.getRange("D"+i).getValue(); //get billing adjustment from row
var dbidrow = gv.dblkup(id); // global function to lookup id and find row position in other ss
var invstat = gv.dbtimetest.getRange("G"+dbidrow).setValue('TRUE'); // set invoiced status
var billadjust = gv.dbtimetest.getRange("M"+dbidrow).setValue(duradj); // set billing adjustment
}
}

Google Sheet - Dynamically change graph bar colour trough a Sheet list

I'm trying to read from column T of my sheet colour that i want to apply to each 2nd series of my graph.
I'm trying different solution:
if i get Value from a single cell and than I apply to option it works. But I would read 100 values copying the same instruction in the code
I wish to create an array (I did) of colour and than apply with a for cycle to each bar of the graph.
Here below the code
function updateColor2() {
var ss = SpreadsheetApp.getActive();
var ws = ss.getActiveSheet();
var charts = ws.getCharts();
var chart = charts[0];
var id = chart.getId();
var colour= ws.getRange('T9:T13').getValues()
//var colour =[];
//var colour = ws.getRange(9, 20).getValue();//
Logger.log(colour);
chart = chart.modify()
//for (var i = 0; i < 4; i++) {
//var colour =[];
//colour.push(ws.getRange(i+9,20).getValue();
.setOption('series.2.items.0.color', colour[0])
.setOption('series.2.items.1.color', colour[1])
.setOption('series.2.items.2.color', colour[2])
.setOption('series.2.items.3.color', colour[3])
.setOption('series.2.items.4.color', colour[4])
// }
.build();
ws.updateChart(chart);
Dynamically add options to chart:
Add an additional option to your graph in each iteration of your loop via modify(), and build it again via build() inside the iteration, like this:
for (let i = 0; i < colour.length; i++) {
chart = chart.modify()
.setOption('series.2.items.' + i + '.color', colour[i])
.build();
}
In this sample, the loop counter (i) is used to identify for the item index from the series and the color index from colour.
Note:
While this is not necessary, it would be appropriate to transform colour into a simple array (getValues() returns a 2D array) so that each colour passed in .setOption is a string value and not an array with a single element. You can use flat() to achieve this:
var colour= ws.getRange('T9:T13').getValues().flat();

(amCharts) The chart gets its data from a HTML table. How to update the legend if a new table row is dynamically added?

I have a HTML table that serves as the data provider for the chart. The table can be dynamically edited with a click of the button (I can add a new row to it).
I can update the chart each time a new row is added. However the legend remains the same, it only has three initial graphs. How can I update the legend alongside the chart?
Here is my fiddle: https://jsfiddle.net/yvzj8acd/2/
And here is the JS where I add new row to the table:
//////////////////////////////////
// This is where I update the chart
//////////////////////////////////
$(document).ready(function() {
var newtr = "<tr class='row1a'><th>Row 4</th><td>10000</td><td>20000</td><td>5000</td><td>15000</td><td>7500</td><td>10000</td></tr>"
var newtr2 = "<tr class='row1a'><th>Row 5</th><td>15000</td><td>30000</td><td>2000</td><td>10000</td><td>15500</td><td>7000</td></tr>"
var newtr3 = "<tr class='row1a'><th>Row 6</th><td>1000</td><td>25000</td><td>15000</td><td>7000</td><td>10000</td><td>8000</td></tr>"
$(".ganti").click(function(e) {
$('#dataTable').append(newtr, newtr2, newtr3);
generateChartData();
chart.dataProvider = chartData;
chart.validateData();
chart.animateAgain();
e.preventDefault();
});
});
Quick FYI, AmCharts.ready is equivalent to $(document).ready, so you can easily combine the two.
As for your question, you need to tweak your data and chart generation approach so that it can handle the dynamically added data. Right now, your setup is pretty much hard-coded to the first three rows and the new data is never added. You also need to update the chart and add additional graphs as needed when new rows are added.
The first thing I did was update your generate data method to dynamically pull all rows that contain data, rather than the current hardcoded method that grabs the first three rows:
function generateChartData() {
// initialize empty array
chartData = [];
// get the table
var table = document.getElementById('dataTable');
var years = table.rows[0].getElementsByTagName('th');
//get the rows with graph values. Since data rows always
//have a class that begin with "row", use that as the query selector
var rows = document.querySelectorAll("tr[class^='row']");
var row;
// iterate through the <td> elements of the first row
// and construct chart data out of other rows as well
for (var x = 0; x < years.length; x++) {
//set up the initial object containing the year
var dataElem = {
"year": years[x].textContent
};
//iterate through the other rows based on the current year column (x + 1) and add that value to the
//object
for (row = 0; row < rows.length; row++) {
dataElem[rows[row].cells[0].textContent] = rows[row].cells[x + 1].textContent
}
//append final object to chart data array
chartData.push(dataElem);
}
}
Next, I created a generateGraphsFromData method that takes the chart instance and chartData array. This method compares the valueFields found in the first element of the chartData array and the valueFields in the chart's graphs array and creates new graphs where there aren't any in the array. This works for both chart creation and update:
//update the chart's graphs array based on the the currently known valueFields
function generateGraphsFromData(chart, chartData) {
//get the chart graph value fields
var graphValueFields = chart.graphs.map(function(graph) {
return graph.valueField;
});
//create an array of new graph value fields by filtering out the categoryField
//and the currently known valueFields.
var newGraphValueFields = Object.keys(chartData[0]).filter(function(key) {
return key != chart.categoryField;
}).filter(function(valueField) {
return graphValueFields.indexOf(valueField) === -1;
});
//for each new value field left over, create a graph object and add to the chart.
newGraphValueFields.forEach(function(valueField) {
var graph = new AmCharts.AmGraph();
graph.title = valueField;
graph.valueField = valueField;
graph.balloonText = "Rp[[value]]";
graph.lineAlpha = 1;
graph.bullet = "round";
graph.stackable = false; // disable stacking
chart.addGraph(graph);
});
}
From there I just updated your ready method to call this function instead of setting the graphs manually, along with forcing the first two to be hidden:
// Create graphs
generateGraphsFromData(chart, chartData);
//default the other two graphs to hidden
chart.graphs[1].hidden = true;
chart.graphs[2].hidden = true;
Then I modified your click event to call the generateGraphs method as well:
$(".ganti").click(function(e) {
$('#dataTable').append(newtr, newtr2, newtr3);
generateChartData();
generateGraphsFromData(chart, chartData);
// ...
Updated fiddle. I also moved the AmCharts.ready method into a separate standalone function and called it into $(document).ready, since both are identical anyway. Feel free to tweak the logic if you want to default other new graphs to hidden or whatever.

Applying DataView on DataTable with duplicate values

I have a table and one of its columns has multiple duplicates. I want to filter all the rows that have the same value on the column at once. I tried to use the getFormattedValue() method which gets the value of a column and then I applied the usual code for DataView. When I click on the row, it disappears all the rows of the table except of the selected one and it doesn't filter all the rows with the duplicate value on the column. Here is my code so far:
var table = new google.visualization.Table(document.getElementById('chart_div'));
var items = " ";
table.draw(dataTable, {width: 1000, height: 300});
google.visualization.events.addListener(table, 'select',
function(event) {
var selection = table.getSelection();
var view = new google.visualization.DataView(dataTable);
for(i = 0; i < row.length; i++){
var items == dataTable.getFormattedValue(i, i);
if(items = "anti-social-behaviour"){
console.log("if statement");
view.setRows([selection[i].row]);
table.draw(view, []);
}
}
});
If anyone could spot the problem it wold be much appreciated. Thank you.
I see many problems with this code. First off, where is row coming from in this line?
for(i = 0; i < row.length; i++){
Second, you are testing the formatted value of (i, i) in the data set, which checks (0, 0), then (1, 1), then (2, 2), etc. instead of checking an entire row or column. Third, on these lines:
view.setRows([selection[i].row]);
table.draw(view, []);
You are setting the rows to be used in the view to the row property of the ith element in selection (which may be undefined, since there is nothing stopping i from growing larger than the length of selection). The setRows method sets the entire row set to use for the view, so each time you call that, you are setting the rows to (a maximum of) 1 row, and then drawing the table with the view, which is why you see only 1 row in the table.
If you want to filter the table to display only rows that have a value matching the value in the selected row, this is what you need to use:
google.visualization.events.addListener(table, 'select', function(event) {
var selection = table.getSelection();
if (selection.length) {
var filterView = new google.visualization.DataView(dataTable);
filterView.setColumns([{
type: 'string',
sourceColumn: 1, // set this column to be whatever column you want to check for duplicate values
calc: 'stringify'
}]);
var value = filterView.getValue(selection[0].row, 0);
var rows = filterView.getFilteredRows([{column: 0, value: value}]);
var view = new google.visualization.DataView(dataTable);
view.setRows(rows);
table.draw(view, []);
}
});

Categories