"Collapse" and sum data table - c#

I have a pivoted data table where the columns are locations and there are around 100. The data is a little strange and I'm looking for an easy way to sum (or collapse the data).
Date, Location 1, Location 2, Location 3
1/1/2001, 6, 0, 0
2/1/2001, 10, 0, 0
1/1/2001, 0, 5, 0
2/1/2001, 0, 4, 0
1/1/2001, 0, 0, 8
2/1/2001, 0, 0, 2
So you can see there are zero fillers so if I was to sum up I'd get unique list of dates and a "collapsed" result that has no zero fillers.
Again, I have about 100 columns and they can't be hardcoded so need a way to sum all these columns dynamically. Is there a trick to do this? Maybe in Linq?

I would use a combination of loops and LINQ:
DataTable pivotedTable = table.Clone(); // same columns, empty
var pivotColumns = pivotedTable.Columns.Cast<DataColumn>().Skip(1).ToList();
var dateGroups = table.AsEnumerable()
.GroupBy(r => r.Field<DateTime>("Date").Date);
foreach(var date in dateGroups)
{
DataRow row = pivotedTable.Rows.Add(); // already added to table now
row.SetField("Date", date.Key);
foreach(DataColumn c in pivotColumns)
row.SetField(c, date.Sum(r => r.Field<int>(c.ColumnName)));
}
Here's your sample data:
DataTable table = new DataTable();
table.Columns.Add("Date", typeof(DateTime));
table.Columns.Add("Location 1", typeof(int));
table.Columns.Add("Location 2", typeof(int));
table.Columns.Add("Location 3", typeof(int));
table.Rows.Add(new DateTime(2001, 1, 1), 6, 0, 0);
table.Rows.Add(new DateTime(2001, 2, 1), 10, 0, 0);
table.Rows.Add(new DateTime(2001, 1, 1), 0, 5, 0);
table.Rows.Add(new DateTime(2001, 2, 1), 0, 4, 0);
table.Rows.Add(new DateTime(2001, 1, 1), 6, 0, 8);
table.Rows.Add(new DateTime(2001, 1, 1), 6, 0, 2);
Output:
Date Location 1 Location 2 Location 3
01.01.2001 00:00:00 18 5 10
01.02.2001 00:00:00 10 4 0

If the first column is always the date and the rest of the columns are always sums you can just iterate the ItemArray on each row
DataTable dt = new DataTable();
var sums = new Dictionary<DateTime, int>();
foreach(DataRow dr in dt.Rows)
{
int sum = 0;
for(int i = 1; i < dr.ItemArray.Length; i++)
{
sum += (int)dr.ItemArray[i];
}
sums.Add((DateTime)dr.ItemArray[0], sum);
}

Related

Stacked Chart Format Syncfusion

I should have 5 columns which stack the second and third values.
Instead, I get two columns:
The first stacking the second value of each column.
The second column stacking the last 4 values.
I'm not sure exactly what I'm doing wrong. I have been battling with Syncfusion for the past week trying to get, what I would have thought to be, fairly basic slides built for a client. However, this has been a nightmare so far.
// Creates the two charts that go on the presentation
IPresentationChart chart = slide.Charts.AddChart(150, 100, 300, 125);
// Set data values
chart.ChartData.SetValue(1, 1, date1);
chart.ChartData.SetValue(2, 1, date2);
chart.ChartData.SetValue(3, 1, date3);
chart.ChartData.SetValue(4, 1, date4);
chart.ChartData.SetValue(5, 1, date5);
chart.ChartData.SetValue(1, 2, mains1);
chart.ChartData.SetValue(2, 2, mains2);
chart.ChartData.SetValue(3, 2, mains3);
chart.ChartData.SetValue(4, 2, mains4);
chart.ChartData.SetValue(5, 2, mains5);
chart.ChartData.SetValue(1, 3, variance1);
chart.ChartData.SetValue(2, 3, variance2);
chart.ChartData.SetValue(3, 3, variance3);
chart.ChartData.SetValue(4, 3, variance4);
chart.ChartData.SetValue(5, 3, variance5);
// Chart 1
// Set data range, Title and Category settings
chart.PrimaryCategoryAxis.CategoryType = OfficeCategoryType.Category;
chart.ChartTitle = "";
chart.ChartArea.Fill.Transparency = 0.5;
IOfficeChartSerie serie= chart.Series.Add(date1);
serie.Values = chart.ChartData[1, 2, 1, 3];
serie.SerieType = OfficeChartType.Column_Stacked;
IOfficeChartSerie serie2 = chart.Series.Add(date2);
serie2.Values = chart.ChartData[2, 2, 2, 3];
serie2.SerieType = OfficeChartType.Column_Stacked;
IOfficeChartSerie serie3 = chart.Series.Add(date3);
serie3.Values = chart.ChartData[3, 2, 3, 3];
serie3.SerieType = OfficeChartType.Column_Stacked;
IOfficeChartSerie serie4 = chart.Series.Add(date4);
serie4.Values = chart.ChartData[4, 2, 4, 3];
serie4.SerieType = OfficeChartType.Column_Stacked;
IOfficeChartSerie serie5 = chart.Series.Add(date5);
serie5.Values = chart.ChartData[5, 2, 5, 3];
serie5.SerieType = OfficeChartType.Column_Stacked;
chart.PlotArea.Layout.ManualLayout.Height = 0.9;
chart.PlotArea.Layout.ManualLayout.Width = 1;
chart.PlotArea.Layout.ManualLayout.Left = 0;
chart.PlotArea.Layout.ManualLayout.Top = 0;
chart.PrimaryCategoryAxis.CategoryLabels = chart.ChartData[1, 1, 5, 1];
chart.Legend.IncludeInLayout = false;
chart.HasLegend = false;
The number of columns in the column-stacked chart depends on the number of categories available in the data range. In your code snippet, there are only two category values. So, two columns are displayed in the column - stacked chart.
Example: serie.Values = chart.ChartData[1, 2, 1, 3];
Here the category values are [ 1, 2 ] and [ 1 , 3 ].
We have also manually created a chart in Microsoft PowerPoint for your scenario (in your code snippet) and attached the created PowerPoint presentation in below link. 
http://www.syncfusion.com/downloads/support/directtrac/general/pp/Chart1370190114.pptx
We have also modified your code snippet to display the 5 columns. Please find the code snippet as below. 
IPresentationChart chart = slide.Charts.AddChart(150, 100, 300, 125); 
chart.ChartType = OfficeChartType.Column_Stacked; 
chart.ChartData.SetValue(1, 1, "4355"); 
chart.ChartData.SetValue(2, 1, "4356"); 
chart.ChartData.SetValue(3, 1, "4357"); 
chart.ChartData.SetValue(4, 1, "4358"); 
chart.ChartData.SetValue(5, 1, "4359"); 
chart.ChartData.SetValue(1, 2, "6"); 
chart.ChartData.SetValue(2, 2, "7"); 
chart.ChartData.SetValue(3, 2, "8"); 
chart.ChartData.SetValue(4, 2, "9"); 
chart.ChartData.SetValue(5, 2, "10"); 
chart.ChartData.SetValue(1, 3, "11"); 
chart.ChartData.SetValue(2, 3, "12"); 
chart.ChartData.SetValue(3, 3, "13"); 
chart.ChartData.SetValue(4, 3, "14"); 
chart.ChartData.SetValue(5, 3, "15"); 
//Set data range, Title and category settings 
chart.PrimaryCategoryAxis.CategoryType = OfficeCategoryType.Category; 
chart.ChartTitle = ""; 
chart.ChartArea.Fill.Transparency = 0.5; 
IOfficeChartSerie serie = chart.Series.Add("date1"); 
//Selecting data from first row second column to fifth row second column 
//ChartData[startRow,startColumn,endRow,endColumn] 
serie.Values = chart.ChartData[1, 2, 5, 2]; //Modified the data range to have 5 columns
serie.SerieType = OfficeChartType.Column_Stacked; 
IOfficeChartSerie serie2 = chart.Series.Add("date2"); 
//Selection data from first row third column to fifth row third column 
serie2.Values = chart.ChartData[1, 3, 5, 3]; //Modified the data range to have 5 columns
serie2.SerieType = OfficeChartType.Column_Stacked; 
chart.PlotArea.Layout.ManualLayout.Height = 0.9; 
chart.PlotArea.Layout.ManualLayout.Width = 1; 
chart.PlotArea.Layout.ManualLayout.Left = 0; 
chart.PlotArea.Layout.ManualLayout.Top = 0; 
chart.PrimaryCategoryAxis.CategoryLabels = chart.ChartData[1, 1, 5, 1]; 
chart.Legend.IncludeInLayout = true; 
chart.HasLegend = false;
Output document for the above modified code snippet: 
http://www.syncfusion.com/downloads/support/directtrac/general/pp/Output-549061229.pptx 
Please let us know if you need further assistance on this, 
Note : I work for Syncfusion Software Private Limited
 

c# subtract duplicates in merge datatables

Code:
// Datatable DB
DataTable CashBillingsArticles_DB = new DataTable();
var CashBillingsArticles_DB_Primary = CashBillingsArticles_DB.Columns.Add("article_id", typeof(Int32));
CashBillingsArticles_DB.Columns.Add("article_quantity", typeof(Double));
CashBillingsArticles_DB.Columns.Add("article_sellprice", typeof(Double));
CashBillingsArticles_DB.Columns.Add("article_discount", typeof(Double));
CashBillingsArticles_DB.Columns.Add("article_isv", typeof(Double));
CashBillingsArticles_DB.Columns.Add("article_total", typeof(Double));
CashBillingsArticles_DB.PrimaryKey = new DataColumn[] { CashBillingsArticles_DB_Primary };
// DB Data
CashBillingsArticles_DB.Rows.Add(1, 1, 5, 0, 0, 5);
CashBillingsArticles_DB.Rows.Add(2, 2, 6, 0, 0, 12);
CashBillingsArticles_DB.Rows.Add(4, 1, 10, 0, 0, 10);
CashBillingsArticles_DB.Rows.Add(7, 2, 3, 0, 0.9, 6.9);
// DataTable Grid
DataTable CashBillingsArticles_Grid = new DataTable();
var CashBillingsArticles_Primary = CashBillingsArticles_Grid.Columns.Add("article_id", typeof(Int32));
CashBillingsArticles_Grid.Columns.Add("article_quantity", typeof(Double));
CashBillingsArticles_Grid.Columns.Add("article_sellprice", typeof(Double));
CashBillingsArticles_Grid.Columns.Add("article_discount", typeof(Double));
CashBillingsArticles_Grid.Columns.Add("article_isv", typeof(Double));
CashBillingsArticles_Grid.Columns.Add("article_total", typeof(Double));
CashBillingsArticles_Grid.PrimaryKey = new DataColumn[] { CashBillingsArticles_Primary };
// Grid Data
CashBillingsArticles_Grid.Rows.Add(2, 1, 6, 0, 0, 6);
CashBillingsArticles_Grid.Rows.Add(3, 1, 1, 0, 0.15, 1.15);
CashBillingsArticles_Grid.Rows.Add(4, 3, 10, 0, 0, 30);
CashBillingsArticles_Grid.Rows.Add(7, 4, 3, 0, 1.8, 13.8);
I need to show this result comparing the article_id column in the tables and always show the article_sellprice value of the datatable CashBillingsArticles_Grid:
id qty price desc isv total
1 -1 5 0 0 -5
2 -1 6 0 0 -6
3 1 1 0 0.15 1.15
4 2 10 0 0 20
7 2 3 0 0.9 6.9
Graphic Illustration:
In the database i have:
The user make some modifications to the invoice and the final result is:
I need to get the differences between the database and the user grid modification, like this: (I need this output)

Filling in missing dates in datatable using a linq group by date query

I am trying to fill missing date in data table for report.
for example:
data table collection :
2010-01-01 : 5
2010-01-02 : 4
2010-01-03 : 2
2010-01-05 : 6
but I want result like this:
2010-01-01 : 5
2010-01-02 : 4
2010-01-03 : 2
2010-01-04 : 0
2010-01-05 : 6
var total = from row in dt2.AsEnumerable()
where row.Field<UInt32>("Super Category") == j
group row by row.Field<string>("Activation Date") into sales
orderby sales.Key
select new
{
Name = sales.Key,
CountOfClients = sales.Count()
};
How can I do that?
This works:
DataTable dt = new DataTable();
dt.Columns.Add(new DataColumn("dt", typeof(DateTime)));
dt.Columns.Add(new DataColumn("num", typeof(int)));
dt.Rows.Add(new DateTime(2010, 1, 1), 5);
dt.Rows.Add(new DateTime(2010, 1, 2), 4);
dt.Rows.Add(new DateTime(2010, 1, 3), 2);
dt.Rows.Add(new DateTime(2010, 1, 5), 6);
dt.Rows.Add(new DateTime(2010, 1, 8), 6);
dt.Rows.Add(new DateTime(2010, 1, 9), 6);
dt.Rows.Add(new DateTime(2010, 1, 12), 6);
DateTime minDT = dt.Rows.Cast<DataRow>().Min(row => (DateTime)row["dt"]);
DateTime maxDT = dt.Rows.Cast<DataRow>().Max(row => (DateTime)row["dt"]);
// Create all the dates that should be in table
List<DateTime> dts = new List<DateTime>();
DateTime DT = minDT;
while (DT <= maxDT)
{
dts.Add(DT);
DT = DT.AddDays(1);
}
// Find the dates that should be in table but aren't
var DTsNotInTable = dts.Except(dt.Rows.Cast<DataRow>().Select(row => (DateTime)row["dt"]));
foreach (DateTime dateTime in DTsNotInTable)
dt.Rows.Add(dateTime, 0);
// Order the results collection
var ordered = dt.Rows.Cast<DataRow>().OrderBy(row => (DateTime)row["dt"]);
// Create a DataTable object
DataTable dt2 = ordered.CopyToDataTable();
dt2 table will contain results without DateTime gaps ordered by DateTime column.
try something like this.
DateTime startDate = collection.first().date;
DateTime endDate = collection.last().date;
While(startDate < endDate)
{
// compare if collection date is valid
startDate.AddDays(1);
}

Long DateTime field to short Datetime field in a Datatable without LINQ getting distinct values

I have a datatable that has many fields from many event files. I need to be able to select distinct on the time. The datetime field is in this format MM/dd/yyyy HH:mm:ss .. I am trying this:
dpall = RollUpValuesFunctions.jobTableFinalCSVOutput.DefaultView.ToTable();
what I am wondering is if there is a way to apply a constraint on the datetime field to only get a distinct date like MM/dd/yyyy HH:mm?
Each minute has a couple hundred records, but for my purposes I only need one of the records from that minute.
And this is in C#
Given the following data:
var dt = new DataTable();
dt.Columns.Add(new DataColumn("Time", typeof(DateTime)));
dt.Rows.Add(new DateTime(2000, 12, 31, 12, 0, 0));
dt.Rows.Add(new DateTime(2000, 12, 31, 12, 0, 0));
dt.Rows.Add(new DateTime(2000, 12, 31, 12, 0, 1));
dt.Rows.Add(new DateTime(2000, 12, 31, 12, 0, 2));
dt.Rows.Add(new DateTime(2000, 12, 31, 12, 4, 2));
dt.Rows.Add(new DateTime(2000, 12, 31, 12, 5, 5));
dt.Rows.Add(new DateTime(2000, 12, 31, 12, 5, 2));
the obvoius Linq approach is
var result = from row in dt.AsEnumerable()
let d = row.Field<DateTime>("Time")
group row by new DateTime(d.Year, d.Month, d.Day, d.Hour, d.Minute, 0) into g
select g.First();
foreach (DataRow row in result)
Console.WriteLine(row["Time"]);
but since you asked for a non-linq solution, why not just iterate over each row, get rid of the seconds part of the time, and use a DataView to do the Distinct part:
foreach (DataRow row in dt.Rows)
{
DateTime d = row.Field<DateTime>("Time");
row["Time"] = new DateTime(d.Year, d.Month, d.Day, d.Hour, d.Minute, 0);
// or use "tricks" like this
// row["Time"] = d.AddTicks(-(d.Ticks % TimeSpan.TicksPerMinute));
}
foreach(DataRow r in new DataView(dt).ToTable(true).Rows)
Console.WriteLine(r["Time"]);
If you don't want to change your DataTable, you can copy it before.

LINQ group by problem

I am facing a problem with LINQ.
Here is the code,
public class TimeObject
{
public DateTime Time { get; set; }
}
private void TestLINQ()
{
List<TimeObject> results = new List<TimeObject>();
results.Add(new TimeObject() { Time = new DateTime(2010, 8, 1, 0, 10, 0)});
results.Add(new TimeObject() { Time = new DateTime(2010, 8, 1, 0, 20, 0)});
results.Add(new TimeObject() { Time = new DateTime(2010, 8, 1, 0, 30, 0)});
results.Add(new TimeObject() { Time = new DateTime(2010, 8, 1, 0, 40, 0)});
results.Add(new TimeObject() { Time = new DateTime(2010, 8, 1, 0, 50, 0)});
results.Add(new TimeObject() { Time = new DateTime(2010, 8, 1, 1, 10, 0)});
results.Add(new TimeObject() { Time = new DateTime(2010, 8, 1, 1, 20, 0)});
results.Add(new TimeObject() { Time = new DateTime(2010, 8, 1, 1, 30, 0)});
results.Add(new TimeObject() { Time = new DateTime(2010, 8, 1, 1, 40, 0)});
results.Add(new TimeObject() { Time = new DateTime(2010, 8, 1, 2, 15, 0)});
results.Add(new TimeObject() { Time = new DateTime(2010, 8, 1, 2, 30, 0)});
var counts = from result in results
group result by result.Time.Date.Hour into groupedResult
select new { Hour = groupedResult.Key, Count = groupedResult.Count() };
foreach (var count in counts)
{
MessageBox.Show(count.Hour + " - " + count.Count);
}
}
The output I expect is
0 - 5,
1 - 4,
2 - 2
But I am always getting 0 - 12. Why it is not grouping by hour?
Please help me. Thanks.
.Date trims off the hour etc. portion, giving you just a date. Try grouping by result.Time.Hour instead.
When you use .Date on a DateTime the time will be set to zero. So use this:
var counts = from result in results
group result by result.Time.Hour into groupedResult
select new { Hour = groupedResult.Key, Count = groupedResult.Count() };
change result.Time.Date.Hour to result.Time.Hour
Try converting group result by result.Time.Date.Hour into the hour value directly using a DateTime conversion rather than using a property of the Time object. The LINQ statement may be stopping the grouping operation at the Time property.

Categories

Resources