I'm using jsRender and I wanted to display my data as columns rather then the rows I am returning. I want to pivot the data - is this something that can be done with jsRender. I can't get the data pivoted in SQL so my only option is to do it myself.
This is basically what I am after. I want to write the column names myself.
Header | Row 1 | Row 2 | Row 3 |
Column Blah | Row data | Row data | Row data
More blah | Row data | Row data | Row data
I have tried to use the {{for}} loop for table cell but I just don't know where to start.
UPDATE:
After Boris's suggestion I have tried the suggested code. Although I as it's not formatting properly I have included it here.
This is an excerpt of my JSON source:
{
"Layers": {
"Layer": [
{
"#LayerID": "1",
"RiskRef": {
"#ColVal": "Contract/Section Number",
"#text": "PUSNA11000392/1"
},
"ContractStatus": {
"#ColVal": "New, Renewal or NTU?",
"#text": "New"
},
"AdjustRate": {
"#ColVal": "Adjustable Rate",
"#text": "0.53%"
},
And my jsRender javaScript code is:
<script id="xolDetailsTemplate" type="text/x-jsrender">
{{for Layers}}
{{for >#data["Layer"]}}
<td>{{>#data["#LayerID"]}}</td>
{{/for}}
{{/for}}
</script>
If your data is
model = { people: [{firstName: "Jo", lastName: "Colby"}, ...] }
you can use the following template to render a people array, pivoted rows to columns:
<tbody>
<tr><td>First Name</td>{{for people}}<td>{{>firstName}}</td>{{/for}}</tr>
<tr><td>Last Name</td>{{for people}}<td>{{>lastName}}</td>{{/for}}</tr>
</tbody>
In your comment below you say that your data is has field names like "#foo". Lets consider this example:
model = { "people": [{"#firstName": "Jo", "#lastName": "Colby"}}, ...] }.
You can render this with a template as follows:
<tbody>
<tr><td>First Name</td>{{for people}}<td>{{>#data["#firstName"]}}</td>{{/for}}</tr>
<tr><td>Last Name</td>{{for people}}<td>{{>#data["#lastName"]}}</td>{{/for}}</tr>
</tbody>
If the field name has non JavaScript name characters, such as "#" that is where you need to use the syntax #data["#xxx"].
Related
I need to loop through the customDimensions.
example: I have scenario like 1000 tags(json objects) inside the customDimensions like below
cust_0: array
cust_1: array
like 1000 tags inside the customDimensions I want to iterate through and get the JSON object is it possible loop in kusto queries?
sample data: As given sample data I stored below in customDimensions like that i have multiple rows, and in that, I want to combine(merge) the array0, array1, array2 how to write the query to merge the records array0 array1 array2 are dynamically generated columns
{
"sample1":"data",
"sample2":"data",
"sample3":"data",
"sample4":"daa",
"sample5":"data",
"sample6":"data",
"array0":[
{
"1":"0",
"2":"1",
"3":"1",
"4":"1 1",
"5":"1 1",
"6":"",
"7":"",
"8":"1(1)",
"9":"1",
"10":"1"
},
"array1": [
{
"1":"0",
"2":"1",
"3":"1",
"4":"1 1",
"5":"1 1",
"6":"",
"7":"",
"8":"1(1)",
"9":"1",
"10":"1"
}
]
},
}
{
"sample1":"data",
"sample2":"data",
"sample3":"data",
"sample4":"daa",
"sample5":"data",
"sample6":"data",
"array0":[
{
"1":"0",
"2":"1",
"3":"1",
"4":"1 1",
"5":"1 1",
"6":"",
"7":"",
"8":"1(1)",
"9":"1",
"10":"1"
},
"array1": [
{
"1":"0",
"2":"1",
"3":"1",
"4":"1 1",
"5":"1 1",
"6":"",
"7":"",
"8":"1(1)",
"9":"1",
"10":"1"
}
]
},
}
you may be able to achieve that using mv-expand or mv-apply.
if you're not sure how, please provide a sample data set (preferably, using the datatable operator), and the expected output for it, with a verbal description of the logic you want to implement
I managed to write an R function using google sheets api's 'setBasicFilter' that turns on the basic filter on the cell range specified by the user. However, when the user tries to 'Filter by values', no values appear to be selected.
The function seems to work fine in the sense that the green filter arrow (or button at the right side of the cells you are using to filter) turn on in the sheets (tabs) and cell range that I want. What I do not understand is why when I click on it, it appears as if there were no values in the column to filter by. It is completely blank but there is data on the column!
I thought perhaps it was setting a filter on top of a filter and tried using 'clearBasicFilter' first before sending 'setBasicFilter' and it did not work.
I tried writing data to the sheet and then turning on the filter and, vice versa, turning on the filter and then writing the data. Still appears as if there were no values to filter by (although the data is there).
In essence, what I am trying to achieve is, when writing data to a sheet:
Remove any basic filter present.
Write the data.
Turn the filter buttons back on (within the range specified by the user of the function).
Here is what my call looks like:
# for each row in the param table create a json body
for(row in nrow(param)){
body <- paste0('{"requests": [
{
"setBasicFilter": {
"filter": {
"range":{"sheetId": ', param$sheetId,
', "startRowIndex": ', param$startRow,
', "endRowIndex": ', param$endRow,
', "startColumnIndex": ', param$startCol,
', "endColumnIndex": ', param$endCol,
'}}}}]}')
}
Where the param table looks like this (and is created based on the users' inputs to the function:
|sheetName |startRow |endRow |startCol |endCol |sheetId |
|:---------|:--------|:------|:--------|:------|:-----------|
|Sheet1 |1 |0 |0 |11 |someId |
|Sheet2 |0 |0 |0 |11 |someOtherId |
Multiple json bodies are created and then I lapply the call using google's batchUpdate method.
I do not get any errors and the filter turns on where I need it to be. I would like to be able to:
Go to the sheet I just turned the filter on
Click on the green filter arrow (which will already be in the cell range I specified)
And see the list of all possible values I can filter that column by
I am sorry for such a long description. I was trying to be as specific as possible.
After re-visiting my function, I found where the issue was. The fix was to default the endRowIndex to the maximum number of rows allowed by google sheets so that all values would be included in the filter range.
For example, with a modified param table that looks like this:
|sheetName |startRow |endRow |startCol |endCol |sheetId |
|:---------|:--------|:------|:--------|:------|:-----------|
|Sheet1 |1 |40000 |0 |11 |someId |
|Sheet2 |0 |40000 |0 |18278 |someOtherId |
And a json body that looks like this:
# for each row in the param table create a json body
for(row in nrow(param)){
body <- paste0('{"requests": [
{
"setBasicFilter": {
"filter": {
"range":{"sheetId": ', param$sheetId,
', "startRowIndex": ', param$startRow,
', "endRowIndex": ', param$endRow,
', "startColumnIndex": ', param$startCol,
', "endColumnIndex": ', param$endCol,
'}}}}]}')
}
The filter will be turned on from A2 to J2 in Sheet1 (all rows included) and A1 to the last column allowed by google sheets (all rows included). I still want the user to be able to specify the end row so I will leave that as an option. An alternative would be to exclude the end row and column indexed in the json body as such:
body <- paste0('{"requests": [
{
"setBasicFilter": {
"filter": {
"range":{"sheetId": ', param$sheetId,
', "startRowIndex": ', param$startRow,
', "startColumnIndex": ', param$startCol
'}}}}]}')
}
How can I dynamically graph points from a table? The table has two columns dedicated to the x and y coordinates and an additional column for duration.
Example
Say I have the table shown below. The Date and Effort columns would compose the x,y coordinates (Timestamp, Effort) for a line chart. This line chart would have data-points based on cumulative totals from the Timestamp and Effort columns. The Duration column would determine how long the entry had an impact on the data-points of the line chart. So based on this table, I would want a line chart with the coordinates listed under Data-Points.
Timestamp Effort Duration
4/13/2016 12:13:12.15 10 100
4/13/2016 12:13:12.80 12 100
4/13/2016 12:13:13.15 30 100
4/13/2016 12:13:13.80 50 100
Data-Points:
(4/13/2016 12:13:12.15, 10)
(4/13/2016 12:13:12.80, 22)
(4/13/2016 12:13:13.15, 42)
(4/13/2016 12:13:13.80, 80)
One of the Highcharts demos shows precisely how you can accomplish this using a static HTML table: http://www.highcharts.com/demo/column-parsed.
I've created a fiddle using your sample data and plotted it as a line chart, per your requirement: http://jsfiddle.net/brightmatrix/qgvkp0t0/
Your data table would be coded similar to the following:
<table id="datatable">
<thead>
<tr>
<th>Time</th>
<th>Effort</th>
</tr>
</thead>
<tbody>
<tr>
<th>4/13/2016 12:13:12.15</th>
<td>10</td>
</tr>
<tr>
<th>4/13/2016 12:13:12.80</th>
<td>22</td>
</tr>
<tr>
<th>4/13/2016 12:13:13.15</th>
<td>42</td>
</tr>
<tr>
<th>4/13/2016 12:13:13.80</th>
<td>80</td>
</tr>
</tbody>
</table>
You would then create your chart using the following code. Note the data attribute that calls to your HTML table's ID:
$(function () {
$('#container').highcharts({
data: {
table: 'datatable'
},
chart: {
type: 'line'
},
title: {
text: 'Data extracted from a HTML table in the page'
},
xAxis: {
type: 'category'
},
yAxis: {
allowDecimals: false,
title: {
text: 'Units'
}
},
tooltip: {
formatter: function () {
return '<b>' + this.series.name + '</b><br/>' + this.point.y;
}
}
});
});
One thing to note: without any modifications, your timestamp data would be read into your x-axis as a datetime type. This would leave you with empty points for those dates or times in between.
In my example, I explicitly set the x-axis type to category, so that there is exactly one plot on the chart for each data point.
Now, to get the x-axis labels to appear in a more readable format, you can explore the xAxis.labels.formatter attribute (http://api.highcharts.com/highcharts#xAxis.labels.formatter) and the Highcharts.dateFormat() function (http://api.highcharts.com/highcharts#Highcharts.dateFormat).
Update (July 13, 2016): I've been able to solve the original poster's requirement of adding cumulative points based on data in an HTML table, but not the removal of dates that are older than the current data point's duration. My modifications are below.
In order to take the data from an HTML table and work with it prior to plotting the chart, I used the data.columns attribute (see http://api.highcharts.com/highcharts#data.columns).
Before your chart options are declared, I wrote this code to go through the HTML table and add the content to arrays.
// read through the HTML table and calculate cumulative effort
// solution inspired by:
// 1. http://stackoverflow.com/questions/3248869/how-do-i-get-data-from-a-data-table-in-javascript
// 2. http://stackoverflow.com/questions/10057226/how-to-get-html-table-td-cell-value-by-javascript
// set the series name as the default; this is the first entry read by data.columns
var seriesTime = ['Time'];
var seriesEffort = ['Effort'];
var seriesDuration = ['Duration'];
var table = document.getElementById('datatable');
var noRows = table.rows.length;
// go through the table and assign values to the series arrays
// start with the second row (r = 1) to omit the table's header
for (var r = 1; r < noRows; r++) {
seriesTime.push(Date.parse(table.rows[r].cells[0].innerHTML));
seriesEffort.push(parseInt(table.rows[r].cells[1].innerHTML));
seriesDuration.push(parseInt(table.rows[r].cells[2].innerHTML));
}
I then use those arrays as follows:
data: {
// refer to demo link at: http://api.highcharts.com/highcharts#data.columns
columns: [
seriesTime, // categories
seriesEffort // first series
]
},
The way this works is that the first item expected in these arrays is the series name, which is why I have as the default value when the arrays are first declared.
Now, once you have the data in arrays, you would need to run through them to calculate your cumulative totals and subtract those whose duration has passed. Unfortunately, I wasn't successful on the duration piece. Here's what I wrote in my fiddle:
// next, go through the series arrays and tally up the cumulative totals based on duration
for (var t = 2; t < seriesTime.length; t++) {
seriesEffort[t]+=seriesEffort[t-1];
for (var e = 1; e < seriesEffort.length; e++) {
if (seriesTime[e] < seriesTime[t] - seriesDuration[t]) { // NOTE: this is where I'm getting stuck
seriesEffort[t]-=seriesEffort[e];
} else {
//seriesEffort[t]+=seriesEffort[e];
}
}
}
I hope this gets you closer to your solution. I apologize that I couldn't get this 100% mocked up for you.
I ended up preprocessing the data. First, I got all of the data points into the series cumulatively. Next, I updated the datetime x values with the durations. Finally, I subtracted each individual y value whose x value was less than the cumulative data point's x value.
I am building a Meteor app where I have to display a variable length table of calculation results. The calculations are done in Meteor and displayed in cells of the rows - each cell in the table is a numeric result based on a complex calculation. Finally I want to display a total calculation for each row.
calcresult1 calcresult2 row1sum
calcresult3 calcresult4 row2sum
:
(variable number of rows)
How can I efficiently calculate the row sums reactively from the calcresults on each row?
Can I setup a single session variable, sum to it when rendering the cells in the row, and then flush the total as each rowsum is to be rendered?
If the rows have the same number of cells each time, you could pass the results from each cell helper to a final helper.
<template name="calcTable">
<table>
{{#each calcRow}}
<tr>
<td>{{calcresult1}}</td>
<td>{{calcresult2}}</td>
<td>{{rowsum calcresult1 calcresult2}}</td>
</tr>
{{/each}}
</table>
</template name="calcTable">
-
Template.calcTable.helpers({
calcresult1: function() {
return result;
},
calcresult2: function() {
return result;
},
rowsum: function(calcresult1, calcresult2) {
return calcresult1 + calcresult2;
}
});
I have an objectdatasource which i want to bind to a repeater.
the problem is, I cannot work out how to display a variable amount of columns with a variable amount of rows.
for example:
The dataset I have is structured like this. The objectdatasource is a List<item>.
item {
string name;
List<itemdata> data;
}
itemdata {
DateTime year;
double amount;
}
so basically i want to make a table
| year | year | year | year
name | amount | amount | amount | amount
name | amount | amount | amount | amount
name | amount | amount | amount | amount
name | amount | amount | amount | amount
The number of items are variable, as well as the number of itemdata that the item contains.
Hope someone can point me in the right direction.
Thanks
The solution to your problem will require three different repeaters, one of which is nested inside another. Begin with the markup like this.
<table>
<tr class="headerRow">
<td> </td>
<asp:Repeater ID="rptYearHeader" runat="server" OnItemDataBound="rptYearHeader_ItemDataBound">
<ItemTemplate>
<td class="header"><asp:Literal ID="litYear" runat="server"></asp:Literal></td>
</ItemTemplate>
</asp:Repeater>
</tr>
<asp:Repeater ID="rptName" runat="server" ItemDataBound="rptName_ItemDataBound">
<ItemTemplate>
<tr>
<td><asp:Literal ID="litName" runat="server"></asp:Literal></td>
<asp:Repeater ID="rptAmounts" runat="server" OnItemDataBound="rptAmounts_ItemDataBound">
<ItemTemplate>
<td><asp:Literal ID="litAmount" runat="server"></asp:Literal></td>
</ItemTemplate>
</asp:Repeater>
</tr>
</ItemTemplate>
</asp:Repeater>
</table>
Binding to this can be a little tricky. The idea is, first we bind the header row - then we bind down the data rows and across the columns. You will want to handle the data binding through code behind using the OnItemDataBound event so that you can wire up the nested repeater with the necessary data.
First we bind the header row with Years. You need to isolate a collection of unique years present in your datasource and keep it in a private variable. You will need to access it during the data binding of the other repeaters later. This will serve as the data source for the header row, creating one cell/column for each year.
List<DateTime> _Years = dataSource.SelectMany(x => x.data).GroupBy(y => y.Year);
rptYear.DataSource = _Years;
rptYear.DataBind();
Now, you need to bind the Name repeater with your original data source. Something like
rptName.DataSource = dataSource;
rptName.DataBind();
This will create one row for each item in your list.
During the OnItemDataBound event for this repeater, you will need to bind the nested repeater to a list of fiscal years - one per each fiscal year in our _Years variable - with any applicable data from the current row's data item. This gets a little tricky, but I'll try to explain:
protected void rptName_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
// get the data item being bound
item currentItem = e.Item.DataItem as item;
// bind the item's name to the literal
//...
//
// get a list of amounts to bind to the nested repeater
// because we cant be sure that every item has amount for all years
// we create a list that we know has all years and plug in the items
// data accordingly.
List<double> amounts = new List<double>();
for (int i = 0; i < _Years.Count; i++)
{
// check whether the current item has data for the year
dataItem di = currentItem.data.Where(d => d.Year == _Years[i]).FirstOrDefault();
if(di == null)
{
// the year did not exist, so we add an amount of 0
amounts.Add(0);
}
else
{
// the year did exist, so we add that year's amount
amounts.Add(di.amount);
}
}
// we now have a list of amounts for all possible years, with 0 filling in
// where the item did not have a value for that year
// bind this to the nested repeater
rptAmounts.DataSource = amounts;
rptAmounts.DataBind();
}
Good luck.
I have had to pull this off with multiple nested repeaters for sub-total and grand total rows before. I started seeing nested repeaters in my sleep.
I suggest you convert your data into a new data structure like this:
name_data {
string name;
int[] amounts;
}
You would then bind your repeater to a List(name_data>.
To create this, first, iterate through item and data lists and keep a list (a List probably) of all the unique years you need to report on. Sort the resulting list so that the years are in order. Now, the indexes of the year list correspond to the column numbers in your output table. Next, iterate through the item list again, this time creating a new name_data object for each item object. The name_data constructor would look like this:
public name_data(string name, int yearCount) {
this.name = name;
amounts = new int[yearCount];
}
The yearCount is the number of items in the year list.
Finally, step through the data list for the current item, look up the year in the year list to get the index, then stick the amount value in the amount field in the corresponding amounts slot. Add your completed name_data to the List.
Once you are done, you should be able to bind your name_data list to your repeater