Datatable sum column by row grouping and show sum result at the end of each group - datatables

I am using datatable row grouping from https://datatables.net/examples/advanced_init/row_grouping.html and it works fine.
I have following scenario:
Now I want to calculate sum(sub-total) by each grouping abd show result in row at the end of grouping as you see in image.
At the end of listing , want to show a final row of total amount.
How to accomplish that.
Js used code is:
$(function() {
var table = $('#table').DataTable({
"columnDefs": [
{ "visible": false, "targets": 1 }
],
"order": [[ 1, 'asc' ]],
"displayLength": 25,
"drawCallback": function ( settings ) {
var api = this.api();
var rows = api.rows( {page:'current'} ).nodes();
var last=null;
api.column(1, {page:'current'} ).data().each( function ( group, i ) {
if ( last !== group ) {
$(rows).eq( i ).before(
'<tr class="group" style="background-color:#F5F5F5;"><td colspan="3">'+group+'</td></tr>'
);
last = group;
}
} );
}
});
});

For the same exemple here : https://datatables.net/examples/advanced_init/row_grouping.html
I have made some changes inside "drawCallback": function ( settings ) {
Check this fiddle https://jsfiddle.net/bLykqbo6/110/
I hope it helps

"drawCallback": function ( settings ) {
var api = this.api();
var rows = api.rows( {page:'current'} ).nodes();
var last = null;
var sum = 0;
var groupColumn = 1; //index of column which you are going to group by.
var amtColumn = 3; // index of column which you are going to sum.
api.column(groupColumn, {page:'current'} ).data().each( function ( group, i ) {
if ( last !== group ) {
api.rows().data().each( function(item){
if (item[groupColumn] == group){
sum = sum + (+item[amtColumn]);
}
});
$(rows).eq( i ).before(
'<tr class="group"><td colspan="2" style="background-color: #e7e7e7;"><b>'+group+'</b></td><td style="background-color: #e7e7e7;"><b>'+ sum +'</b></td></tr>'
);
last = group;
sum = 0;
}
});
}

Related

Using select2 with datatables to filter on multple values

Im currently trying to filter each column of my datatable using a select2 dropdown.
My code currently filters correctly without the multiple tag on the select tag, but once I add the multiple value to my select tag I get the following error:
TypeError: a.replace is not a function
I have been trying to adapt the following datatables javascript:
link
Here is what I have currently:
$('#caseTable').DataTable( {
initComplete: function () {
var x = 0;
this.api().columns().every( function () {
var column = this;
var select = $('<select class="search2" multiple="multiple"><option value=""></option></select>')
.appendTo( $('.dropdown'+x))
.on( 'change', function () {
var val = $.fn.dataTable.util.escapeRegex(
$(this).val()
);
column
.search( val ? '^'+val+'$' : '', true, false ) //find this value in this column, if it matches, draw it into the table.
.draw();
} );
column.data().unique().sort().each( function ( d, j ) {
select.append( '<option id="'+d+'"value="'+d+'">'+d+'</option>' )
} );
x++
} );
$(".search2").select2();
}
} );
EDIT: I managed to fix it. I needed to store the multiple values into an array, and then pipe the values together to search for each individual value.
Here are the changes I made to the .on('change') function.
.on( 'change', function () { //when an option is selected
var val = new Array();
//set val to current element in the dropdown.
val = $(this).val();
if (val.length > 1){
valString = val.toString();
valPiped = valString.replace(/,/g,"|")
column
.search( valPiped ? '^'+valPiped+'$' : '', true, false ) //find this value in this column, if it matches, draw it into the table.
.draw();
} else if (val.length == 1) {
column
.search( val ? '^'+val+'$' : '', true, false ) //find this value in this column, if it matches, draw it into the table.
.draw();
} else {
column
.search('',true,false)
.draw();
}
} );
I managed to fix it. I needed to store the multiple values into an array, and then pipe the values together to search for each individual value. Here are the changes I made to the .on('change') function.
.on( 'change', function () { //when an option is selected
var val = new Array();
//set val to current element in the dropdown.
val = $(this).val();
if (val.length > 1){
valString = val.toString();
valPiped = valString.replace(/,/g,"|")
column
.search( valPiped ? '^'+valPiped+'$' : '', true, false ) //find this value in this column, if it matches, draw it into the table.
.draw();
} else if (val.length == 1) {
column
.search( val ? '^'+val+'$' : '', true, false ) //find this value in this column, if it matches, draw it into the table.
.draw();
} else {
column
.search('',true,false)
.draw();
}
} );

Pivot data dynamically for google line chart

I want to display "population" of various countries through the years in the same line chart. The data displayed is based on selections from a multi-select dropdown "Countries". Underlying Data Table has 3 columns:
Year, Country, Population
2012,countryA,33
2013,countryA,35
2014,countryA,40
2012,countryB,65
2013,countryB,70
2014,countryB,75
2012,countryC,15
2013,countryC,20
2014,countryC,25
I am trying to create a pivoted Data View from the underlying Data Table
The code I am using is:
function drawLineChart() {
var arr = $('#country').val();
var lineChartJson = $.ajax({
url: "../json/lineChart.json",
dataType: "json",
async: false
}).responseText;
var lineChartData = new google.visualization.DataTable(lineChartJson);
var view = new google.visualization.DataView(lineChartData);
var viewCols = [0];
for(var i = 0; i < arr.length; i++) {
var viewCols1 = [{
type: 'number',
label: arr[i],
calc: function (dt, row) {
return (dt.getValue(row, 1) == arr[i]) ? dt.getValue(row, 2) : null;
}
}];
viewCols = viewCols.concat(viewCols1);
}
view.setColumns(viewCols);
var aggCols = [{
column: 1,
type: 'number',
label: view.getColumnLabel(1),
aggregation: google.visualization.data.sum
}];
for(var i = 2; i < 4; i++) {
var aggCols1 = [{
column: i,
type: 'number',
label: view.getColumnLabel(i),
aggregation: google.visualization.data.sum
}];
aggCols = aggCols.concat(aggCols1);
}
var pivotedData = google.visualization.data.group(view, [0], aggCols);
But this does not seem to work as expected and I just get 1 Line in the chart with values for all countries added up (although I can see the legend for 3 countries)
On the other hand if I set my View columns as below, it works as expected.
view.setColumns([0, {
type: 'number',
label: arr[0],
calc: function (dt, row) {
return (dt.getValue(row, 1) == arr[0]) ? dt.getValue(row, 2) : null;
}
}, {
type: 'number',
label: arr[1],
calc: function (dt, row) {
// return values of C only for the rows where B = "bar"
return (dt.getValue(row, 1) == arr[1]) ? dt.getValue(row, 2) : null;
}
}, {
type: 'number',
label: arr[2],
calc: function (dt, row) {
return (dt.getValue(row, 1) == arr[2]) ? dt.getValue(row, 2) : null;
}
}]);
What is going wrong in the loop? Is something wrong with "concat" in the loop where I am creating View Columns? I also saw the viewCols array by using console.log and it seems to have the right elements
I was trying to follow the below post:
Creating pivoted DataView from existing google charts DataTable object
the problem has to do with scope
arr[i] is undefined within calc: function (dt, row)
here is another way to pivot the data...
google.charts.load('current', {
callback: function () {
var arr = [
'countryA',
'countryB',
'countryC'
];
var lineChartData = google.visualization.arrayToDataTable([
['Year', 'Country', 'Population'],
[2012,'countryA',33],
[2013,'countryA',35],
[2014,'countryA',40],
[2012,'countryB',65],
[2013,'countryB',70],
[2014,'countryB',75],
[2012,'countryC',15],
[2013,'countryC',20],
[2014,'countryC',25]
]);
// sort by year
lineChartData.sort([{column: 0}]);
// get unique countries
var countryGroup = google.visualization.data.group(
lineChartData,
[1]
);
// build country data table
var countryData = new google.visualization.DataTable({
cols: [
{label: 'Year', type: 'number'},
]
});
// add column for each country
for (var i = 0; i < countryGroup.getNumberOfRows(); i++) {
countryData.addColumn(
{label: countryGroup.getValue(i, 0), type: 'number'}
);
}
// add row for each year / country
var rowYear;
var rowIndex;
for (var i = 0; i < lineChartData.getNumberOfRows(); i++) {
if (rowYear !== lineChartData.getValue(i, 0)) {
rowYear = lineChartData.getValue(i, 0);
rowIndex = countryData.addRow();
countryData.setValue(rowIndex, 0, rowYear);
}
for (var x = 1; x < countryData.getNumberOfColumns(); x++) {
if (countryData.getColumnLabel(x) === lineChartData.getValue(i, 1)) {
countryData.setValue(rowIndex, x, lineChartData.getValue(i, 2));
}
}
}
// draw agg table
new google.visualization.ChartWrapper({
chartType: 'Table',
containerId: 'table-div',
dataTable: countryData
}).draw();
// draw line chart
new google.visualization.ChartWrapper({
chartType: 'LineChart',
containerId: 'chart-div',
dataTable: countryData
}).draw();
},
packages: ['corechart', 'table']
});
<script src="https://www.gstatic.com/charts/loader.js"></script>
<div id="table-div"></div>
<div id="chart-div"></div>
I could figure out the problem with my code above.
"calc" is the callback function in loop. So only last value of loop variable "i" is visible within the loop.
Putting a wrapper function fixes it:
for(var i = 0; i <= arr.length; i++)(function(i) {
var viewCols1 = [{
type: 'number',
label: arr[i],
calc: function (dt, row) {
return (dt.getValue(row, 1) == arr[i]) ? dt.getValue(row, 2) : null;
}
}];
viewCols = viewCols.concat(viewCols1);
})(i);

Add Commas to a Sum LinQ

I have a Group by Sum operation, but i would like to add ',' every 3 digits
right now i get this output:
{ Pdv = REST, Total = $20786 }
But i would like this Output:
{ Pdv = REST, Total = $2,0786 }
This is my Linq Group By:
foreach (var item in Model.Select(x => new //here you count your total
{
Rid = x.Rid,
Total = x.Total
})
.GroupBy(l => l.Rid) //and then grouping
.Select(z => new
{
Turno = z.Key,
Total = "$" + Decimal.Round(z.Sum(l => l.Total), 0)
}))
{
//Loop Code
}
Your "Total" must be String.
String.Format("{#,##0.00}", 1243.50); // Outputs "1,243.50"
String.Format("{0:$#,##0.00;($#,##0.00);Zero}", 1243.50); // Outputs "$1,243.50"
String.Format("{0:$#,##0.00;($#,##0.00);Zero}", -1243.50); // Outputs"($1,243.50)"
String.Format("{0:$#,##0.00;($#,##0.00);Zero}", 0); // Outputs “Zero"

Create a running sum graph in dc.js

I am trying to create a running sum in crossfilter to use with dc.js.
I have a set of records like the following :
records = [{"date":"2014-01-01","field1":"value1","field2":"value11","value_field":-20},
{"date":"2014-01-02","field1":"value2","field2":"value12","value_field":100},
{"date":"2014-01-03","field1":"value1","field2":"value11","value_field":-10},
{"date":"2014-01-04","field1":"value2","field2":"value12","value_field":150},
]
So far I have created a barGraph which plays nicely with the other dimensions but I would like to be able to show an line graph of the theoretical field runnning_total (along the dimension date).
To have it done in crossfilter would allow me to then group using the fieldx fields and easily get the same running total graph but restricted to a subgroup of values using dc.js.
Any help is welcome.
Since you are grouping across date (as per your date dimension), the reduce() function would be used to perform aggregations grouped by date, as per the highlighted cells in my Mickey Mouse example below:
With a running total you'd need to perform an entirely different operation, looping down the rows:
You can aggregate the data and then append the running total field as follows. I've also included an example of how to calculate an average value, using the reduce function:
records = [{ "date": "2014-01-01", "field1": "value1", "field2": "value11", "value_field": -20 },
{ "date": "2014-01-02", "field1": "value2", "field2": "value12", "value_field": 100 },
{ "date": "2014-01-03", "field1": "value1", "field2": "value11", "value_field": -10 },
{ "date": "2014-01-04", "field1": "value2", "field2": "value12", "value_field": 150 }
];
var cf = crossfilter(records);
var dimensionDate = cf.dimension(function (d) {
return d.date;
});
function reduceAdd(p, v) {
p.total += v.value_field;
p.count++;
p.average = p.total / p.count;
return p;
}
function reduceRemove(p, v) {
p.total -= v.value_field;
p.count--;
p.average = p.count ? p.total / p.count : 0;
return p;
}
function reduceInitial() {
return {
total: 0,
count: 0,
average: 0,
};
}
var average = dimensionDate.group().reduce(reduceAdd, reduceRemove, reduceInitial).all();
var averageWithRunningTotal = appendRuningTotal(average);
function appendRuningTotal(average) {
var len = average.length,
runningTotal = 0;
for (var i = 0; i < len; i++) {
runningTotal += average[i].value.total;
average[i].RunningTotal = runningTotal;
}
return average;
}
And this returns the following:
{"key":"2014-01-01","value":{"total":-20,"count":1,"average":-20},"RunningTotal":-20}
{"key":"2014-01-02","value":{"total":100,"count":1,"average":100},"RunningTotal":80}
{"key":"2014-01-03","value":{"total":-10,"count":1,"average":-10},"RunningTotal":70}
{"key":"2014-01-04","value":{"total":150,"count":1,"average":150},"RunningTotal":220}
Well I know the op already built a solution but after struggling with for a while I was able to crack it, so posting it here if someone else searches for it.
using the cumulative for the following: https://groups.google.com/forum/#!topic/dc-js-user-group/W9AvkP_dZ0U
Running Sum:
var _group = dim.group().reduceSum(function(d) {return 1;});
var group = {
all:function () {
var cumulate = 0;
var g = [];
_group.all().forEach(function(d,i) {
cumulate += d.value;
g.push({key:d.key,value:cumulate})
});
return g;
}
};
for Trailing Twelve Month calculation:
var _group = dateDim.group().reduceSum(function(d) {return d.revenue;});
var group = {
all:function () {
var g = [];
_group.all().forEach(function(d,i) {
var cumulate = 0;
var monthcount =0;
var dt =new Date( d.key);
var ct = new Date(d.key);
ct.setFullYear(ct.getUTCFullYear() -1);
_group.all().forEach(function(d2,i) {
var dt2 = new Date(d2.key);
if(dt2 <= dt && dt2 > ct){
cumulate += d2.value;
monthcount++;
}
})
if(monthcount>=11){
console.log( ' Dt ' + dt + ' ' + cumulate + ' months ' + monthcount);
g.push({key:d.key,value:cumulate})
}
});
return g;
}
};

How i can change the sort order (from default ASC to DESC) if i use custom sorting function in DataTables?

I need perform sorting by date (dd.mm.YYYY H:i:s) in DataTables grid.
I already found DataTables sorting plugin and it works well - but i cant understand how i can change default sort order to descending for sorting plugin results.
I initialize datatables with this code:
$('.dt_table').dataTable( {
"sDom": "<'row'<'span6'l><'span6'f>r>t<'row'<'span6'i><'span6'p>>",
"sPaginationType": "bootstrap",
"aoColumns": [
{ "sType": "date-euro" },
null,
null,
null,
null,
null,
null
],
"iDisplayLength": 25,
"oLanguage": {
"sUrl": "/js/dt_ru.txt"
},
"fnDrawCallback": function() {
$(".editable").editable();
}
} );
And the code of sorting plugin is here:
jQuery.extend( jQuery.fn.dataTableExt.oSort, {
"date-euro-pre": function ( a ) {
if ($.trim(a) != '') {
var frDatea = $.trim(a).split(' ');
var frTimea = frDatea[1].split(':');
var frDatea2 = frDatea[0].split('.');
var x = (frDatea2[2] + frDatea2[1] + frDatea2[0] + frTimea[0] + frTimea[1] + frTimea[2]) * 1;
} else {
var x = 10000000000000; // = l'an 1000 ...
}
return x;
},
"date-euro-asc": function ( a, b ) {
return a - b;
},
"date-euro-desc": function ( a, b ) {
return b - a;
}
} );
Use aaSorting option to specify the sorting by: http://datatables.net/ref#aaSorting.
$('.dt_table').dataTable( {
"aaSorting": [[0, 'desc']]
} );