C# - How To Mathematical Operations In DataTable - c#

I have a DataTable and I want to do some mathematical operations before adding them to DataGridView. First one, I want to find maximum number of third column of my DataTable, second one I want to divide all values in third column with this maximum number. After all I want to replace my new values instead old values. My DataTable looks like that;
Column 1 Column 2 Column 3
---------------------------------------
a b 2000000
q r 250000
s t 185000
m w 400000
o p 750000
After the operations, my DataTable should look like that;
Column 1 Column 2 Column 3
---------------------------------------
a b 1
q r 0.125
s t 0.0925
m w 0.0002
o p 0.375
It's my code;
connection.Open();
//Some insignificant operations here.
for (int q = 0; q < w.Length; q++)
{
for (int a = q + 1; a < w.Length; a++)
{
string query = ".....";
SqlDataAdapter myAdapter = new SqlDataAdapter(query, connection);
DataTable myTable = new DataTable();
myAdapter.Fill(myTable);
//I started here for finding maximum number in DataTable.
int[] myColumn = dt.AsEnumerable().Select(x => x.Field<int>("Column3")).ToArray();
int myMaximum = myColumn.Max();
//I don't know what should I do after that.
foreach (DataRow myRows in myTable.Rows)
{
//Some significant operations again...
dgv1.Rows.Add(...);
}
}
}
connection.Close();

I would suggest get rid of DataTable and work with a plain c# classes.
DataTable is "heavy" structure which do much more than you need and in most of the cases you don't need it.
Create class to represent your data
public class Item
{
public string Column1 { get; set; }
public string Column2 { get; set; }
public int Column3 { get; set; }
}
Load data
public List<Item> LoadData()
{
var query = "SELECT Column1, Column2, Column3 FROM Table";
using (var connection = new SqlConnection(connectionString))
using (var command = connection.CreateCommand())
{
command.CommandType = CommandType.Text;
command.CommandText = query;
using (var reader = command.ExecuteReader())
{
var data = new List<Item>();
while(reader.Read())
{
var item = new Item
{
Column1 = reader.GetString(0),
Column1 = reader.GetString(1),
Column1 = reader.GetInt32(2)
};
data.Add(item);
}
return data;
}
}
}
Create class which will represent calculated data
public class CalculatedItem
{
public string Column1 { get; }
public string Column2 { get; }
public decimal Calculated { get; }
public CalculatedItem(Item item, decimal maxValue)
{
Column1 = item.Column1;
Column2 = item.Column2;
Calculated = (decimal)item.Column3 / maxValue
}
}
Usage
var data = LoadData();
var maxValue = data.Select(item => item.Column3).Max();
var calculatedData = data.Select(item => new CalculatedItem(item, maxValue)).ToList();
// Bind data to the DataGridView
DataGridView1.DataSource = calculatedDate;

Give this a go:
var max = myTable.Rows.Cast<DataRow>().Max(r => r.Field<double>("Column 3"));
foreach (var row in myTable.Rows.Cast<DataRow>())
{
row["Column 3"] = row.Field<double>("Column 3") / max;
}
Runable test code:
var myTable = new DataTable();
myTable.Columns.Add("Column 3", typeof(double));
myTable.Rows.Add(20_00_000);
myTable.Rows.Add(250_000);
myTable.Rows.Add(185_000);
myTable.Rows.Add(400_000);
myTable.Rows.Add(750_000);
Console.WriteLine(String.Join(", ", myTable.Rows.Cast<DataRow>().Select(r => r.Field<double>("Column 3"))));
var max = myTable.Rows.Cast<DataRow>().Max(r => r.Field<double>("Column 3"));
foreach (var row in myTable.Rows.Cast<DataRow>())
{
row["Column 3"] = row.Field<double>("Column 3") / max;
}
Console.WriteLine(String.Join(", ", myTable.Rows.Cast<DataRow>().Select(r => r.Field<double>("Column 3"))));
This outputs:
2000000, 250000, 185000, 400000, 750000
1, 0.125, 0.0925, 0.2, 0.375
Update based on "Column 3" being an int.
Console.WriteLine(String.Join(", ", myTable.Rows.Cast<DataRow>().Select(r => r.Field<int>("Column 3"))));
int max = myTable.Rows.Cast<DataRow>().Max(r => r.Field<int>("Column 3"));
double[] results = myTable.Rows.Cast<DataRow>().Select(r => (double)r.Field<int>("Column 3") / max).ToArray();
Console.WriteLine(String.Join(", ", results));
Now just index in to results when you build your DataGridView.

Have you considered adding a 4th column to the existing table? Make the column an “Expression” column to do the math as you describe, then hide the third column?
An example of this is below…
DataTable GridTable;
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
GridTable = GetDataTable();
FillTable(GridTable);
AddExpressionColumn(GridTable);
dataGridView1.DataSource = GridTable;
dataGridView1.Columns["Column3"].Visible = false;
}
private DataTable GetDataTable() {
DataTable dt = new DataTable();
dt.Columns.Add("Column1", typeof(string));
dt.Columns.Add("Column2", typeof(string));
dt.Columns.Add("Column3", typeof(int));
return dt;
}
private void FillTable(DataTable dt) {
dt.Rows.Add("a", "b", 2000000);
dt.Rows.Add("q", "r", 250000);
dt.Rows.Add("s", "t", 185000);
dt.Rows.Add("m", "w", 400000);
dt.Rows.Add("o", "p", 750000);
}
private void AddExpressionColumn(DataTable dt) {
DataColumn expCol = new DataColumn("Result", typeof(decimal));
dt.Columns.Add(expCol);
string expressionString = "Column3 / " + GetMaxValue(dt).ToString();
expCol.Expression = expressionString;
}
private int GetMaxValue(DataTable dt) {
int max = (int)dt.Compute("Max(Column3)", "");
if (max == 0)
return 1;
return max;
}

Related

linq cast error

I'm trying to get some data from DataTable using Linq, but it gives me following error:
Specified cast is not valid.
First of all, i'm using this to paste copied cells from excel to datagridview
private void btnExcel_Click(object sender, EventArgs e)
{
dataGridView1.Columns.Clear();
dataGridView1.Columns.Add("Column1", "Column1");
dataGridView1.Columns.Add("Column2", "Column2");
dataGridView1.Columns.Add("Column3", "Column3");
dataGridView1.Columns.Add("Column4", "Column4");
dataGridView1.Columns.Add("Column5", "Column5");
dataGridView1.Columns.Add("Column6", "Column6");
dataGridView1.Columns.Add("Column7", "Column7");
dataGridView1.Columns.Add("Column8", "Column8");
dataGridView1.Columns.Add("Column9", "Column9");
dataGridView1.Columns.Add("Column10", "Column10");
dataGridView1.Columns.Add("Column11", "Column11");
dataGridView1.Columns.Add("Column12", "Column12");
dataGridView1.Columns.Add("Column13", "Column13");
DataObject o = (DataObject)Clipboard.GetDataObject();
if (o.GetDataPresent(DataFormats.UnicodeText))
{
if (dataGridView1.RowCount > 0)
dataGridView1.Rows.Clear();
string[] pastedRows = Regex.Split(o.GetData(DataFormats.UnicodeText).ToString().TrimEnd("\r\n".ToCharArray()), "\r\n");
int j = 0;
foreach (string pastedRow in pastedRows)
{
string[] pastedRowCells = pastedRow.Split(new char[] { '\t' });
dataGridView1.Rows.Add();
int myRowIndex = dataGridView1.Rows.Count - 1;
using (DataGridViewRow myDataGridViewRow = dataGridView1.Rows[j])
{
for (int i = 0; i < pastedRowCells.Length; i++)
myDataGridViewRow.Cells[i].Value = pastedRowCells[i];
this.dataGridView1.AutoResizeColumns();
}
j++;
}
}
}
Then I'm using this method to convert datagridview to DataTable
private DataTable GetDataTableFromDGV(DataGridView dgv)
{
var dt = new DataTable();
foreach (DataGridViewColumn column in dgv.Columns)
{
if (column.Visible)
{
// You could potentially name the column based on the DGV column name (beware of dupes)
// or assign a type based on the data type of the data bound to this DGV column.
dt.Columns.Add();
}
}
object[] cellValues = new object[dgv.Columns.Count];
foreach (DataGridViewRow row in dgv.Rows)
{
for (int i = 0; i < row.Cells.Count; i++)
{
cellValues[i] = row.Cells[i].Value;
}
dt.Rows.Add(cellValues);
}
return dt;
}
after that using this linq query to get data and display it to datagridview2
DataTable dt = GetDataTableFromDGV(dataGridView1);
//foreach (DataColumn c in dt.Columns)
//{
// MessageBox.Show(c.ColumnName);
//}
var groupedData = from b in dt.AsEnumerable()
group b by b.Field<int>("Column2") into g
select new
{
column2 = g.Key,
column13 = g.Sum(x => x.Field<decimal>("Column13"))
};
foreach (var result in groupedData)
{
dataGridView2.Rows.Add(result);
}
It throws a " Specified cast is not valid."
Basically what I want is shows on picture below:
pic
You never assign a type to any of the DataColumns that you show, so they will have a default data type of string, and calling Field<int> or Field<decimal> will throw an invalid cast exception.
Either assign the appropriate types to the columns when creating the data table or parse the string values:
var groupedData = from b in dt.AsEnumerable()
group b by int.Parse(b.Field<string>("Column2")) into g
select new
{
column2 = g.Key,
column13 = g.Sum(x => decimal.Parse(x.Field<string>("Column13")))
};

How to add value to a value of certain clolumn and row in datatable C#

I have two datatables:
1.dtEmployee:
|agent_id|agent_name|sum|
2.dtReport:
|sale_date|agent_id|sum |
------------------------
For each record in dtReport I need to find agent_id in dtEmployee and add the value of dtReport["sum"] to dtEmployee["sum"]:
foreach (DataRow r in dtReport)
{
DataRow empRow = dtEmployee.find(dtReport["agent_id"]);
empRow["sum"] += r["sum"];
}
Is there a way that would allow me to accomplish this?
Something like this works:
private void AddValue(string agent_id, decimal sum)
{
DataRow[] row= dtEmployee.Select("agent_id= '"+agent_id+"'");
//since only one record with this agent_id, we take first record of array -> row[0]
decimal dSum= Convert.ToDecimal(row[0][column]);
dSum+= sum;
row[0]["sum"] = dSum;
}
and insert this function into loop:
foreach (DataRow r in dtReport)
{
AddValue(r["agent_id"], r["sum"]);
}
This can be achieved in many ways.
Option 1 :
foreach(DataRow row in dtEmployee.Rows)
{
var update = dtReport.AsEnumerable().FirstOrDefault(r => r.Field<string>("agent_id") == row.Field<string>("agent_id"));
if(update !=null)
row.SetField<float>("sum", update.Field<float>("sum"));
}
Option 2
Another option would be creating new table by joining DataTables
var results = from t1 in dtEmployee.AsEnumerable()
join t2 in dtReport.AsEnumerable()
on t1.Field<int>("agent_id") equals t2.Field<int>("agent_id")
select new { t1, t2 };
// Now we can construct new DataTable
DataTable result = new DataTable() ;
result.Columns.Add("agent_id", typeof(System.Int32));
result.Columns.Add("Name", typeof(System.String));
result.Columns.Add("sum", typeof(float));
foreach(var dr in results )
{
DataRow newRow = results.NewRow();
newRow["agent_id"] = dr.t1.Field<int>("agent_id");
newRow["agent_name"] = dr.t1.Field<string>("agent_name");
newRow["sum"] = dr.t2.Field<float>("sum");
// When all columns have been filled in then add the row to the table
results.Rows.Add(newRow);
}
Working sample
Hope this helps !
You could try something like this. Given that your agent_id and sum are integer.
foreach (DataRow r in dtReport.Rows)
{
dtEmployee.Select(string.Format("agent_id = {0}", r["agent_id"])).ToList<DataRow>().ForEach(
v => { v["sum"] = (v.IsNull("sum") ? 0 : v.Field<int>("sum")) + (r.IsNull("sum") ? 0 : r.Field<int>("sum")); });
}
Or equivalent code
foreach (DataRow r in dtReport.Rows)
{
DataRow[] empRow = dtEmployee.Select("agent_id = " + r["agent_id"]);
for (int i = 0; i < empRow.Length; i++)
{
empRow[i]["sum"] = (empRow[i].IsNull("sum") ? 0 : (int)empRow[i]["sum"]) + (r.IsNull("sum") ? 0 : (int)r["sum"]);
}
}

datatable turns empty when i pass it to method

So i have a very simple function which goes through a table and find those rows that has the age columns between 2 numbers and then return a new table with the new rows. here is my function :
private DataTable AgeFinder(int MiniAge, int MaxiAge, DataTable x)
{
//AFWT : Analyze From Which Table
int count;
DataTable dtAge = new DataTable();
dtAge = x.Clone();
int ageMin = MiniAge;
int ageMax = MaxiAge;
if (ageMin > ageMax)
{
int temp = ageMax;
ageMax = ageMin;
ageMin = temp;
}
int ageDr;
count = 0;
ageDr = 0;
dataGridView1.DataSource = x;
foreach (DataRow dr in x.Rows)
{
ageDr = Convert.ToInt16(x.Rows[count]["age "]);
if (ageDr >= ageMin && ageDr < ageMax)
{
dtAge.Rows.Add(dr.ItemArray);
}
count++;
}
lblCountThisQuery.Text = dtAge.Rows.Count.ToString();
return dtAge;
}
and here is how i call it:
dtAge = AgeFinder(Convert.ToInt16(tbxMinAge.Text), Convert.ToInt16(tbxMaxAge.Text), AT);
dataGridView1.DataSource = dtAge;
now i can see that the AT is not empty but when i check the x (the table inside the method), its empty! why does this happen?

Making my SQLite printing method dynamic

I am using SQLite to store the data for my application.
The application has a UI that loads the data from the SQLite database to display it table by table to the user. Basically the user can click left or right and view the other tables one by one.
The user can also click a button that will show a print preview of the data and let them print it. I have this working, but I am having some issues devising a dynamic way to display ANY table on the print preview screen perfectly. My concerns are if some column titles are too long etc, basically how to calculate the size of each column etc. Should I loop through and find the max character size of any text in the entire column and then set the column width to just wider than that or is there an easier way to do this?
I also write the data-table to a comma separated csv file so it might be a better alternative to use an existing solution to print from a csv file if any of you know such a solution then please suggest it!
Anyway here is the currently existing code:
// ------------------------ code that gets called when the print button is clicked ----------------------------
// holds the row data
List<string[]> myList = new List<string[]>();
if(ReportPage == 1)
{
int rowCount = MyTestTable.RowCount;
for(int i = 0; i <rowCount; i++)
{
MyTestTable.SelectedRowIndex = i;
var row1 = MyTestTable.GetSelectedRow();
var cols1 = row1.ItemArray;
string Col1 = cols1[row1.FindIndexOfColumn("Col1")].ToString();
string Col2 = cols1[row1.FindIndexOfColumn("Col2")].ToString();
string Col3 = cols1[row1.FindIndexOfColumn("Col3")].ToString();
string Col4 = cols1[row1.FindIndexOfColumn("Col4")].ToString();
string Col5 = cols1[row1.FindIndexOfColumn("Col5")].ToString();
string Col6 = cols1[row1.FindIndexOfColumn("Col6")].ToString();
string Col7 = cols1[row1.FindIndexOfColumn("Col7")].ToString();
myList.Add(new string[] { Col1, Col2, Col3, Col4, Col5, Col6, Col7 });
}
string[] cols = new string[7];
cols[0] = "Col1";
cols[1] = "Col2";
cols[2] = "Col3";
cols[3] = "Col4";
cols[4] = "Col5";
cols[5] = "Col6";
cols[6] = "Col7";
PrintUtility.SetUpDocument("TEST", cols, myList);
}
PrintUtility.TestNewReport();
// ---------------------- plugin code that gets called from the application
namespace PrintUtility
{
public class PrintUtility : UserComponentBase
{
public PrintDocument document;
public DataGridView dataGridView;
public PrintUtility()
{
document = new PrintDocument();
dataGridView = new DataGridView();
}
public void SetUpDocument(string title, string[] cols, List<string[]> rows)
{
document = new PrintDocument();
dataGridView = new DataGridView();
document.DocumentName = title;
document.DefaultPageSettings.Landscape = true;
document.PrintPage += PrintPage;
DataGridView dataGrid = new DataGridView();
dataGrid.ColumnCount = cols.Length;
for (int i = 0; i < cols.Length; i++ )
{
dataGrid.Columns[i].Name = cols[i];
}
foreach(string[] row in rows)
{
dataGrid.Rows.Add(row);
}
this.dataGridView = dataGrid;
document.DocumentName = title;
document.PrintPage += PrintPage;
}
public PrintDocument GetDocument()
{
return this.document;
}
private DataTable ConvertListToDataTable(List<string[]> list)
{
// New table.
DataTable table = new DataTable();
// Get max columns.
int columns = 0;
foreach (var array in list)
{
if (array.Length > columns)
{
columns = array.Length;
}
}
// Add columns.
for (int i = 0; i < columns; i++)
{
table.Columns.Add();
}
// Add rows.
foreach (var array in list)
{
table.Rows.Add(array);
}
return table;
}
public void TestNewReport()
{
Report report = new Report(new PdfFormatter());
FontDef fd = new FontDef(report, "Helvetica");
FontProp fp = new FontPropMM(fd, 4);
FontProp fp_Title = new FontPropMM(fd, 6);
FontProp fp_Word = new FontPropMM(fd, 3);
fp_Title.bBold = true;
List<string> columns = new List<string>();
foreach (DataGridViewColumn column in dataGridView.Columns)
{
columns.Add(column.Name.ToString());
}
List<List<string>> rows = new List<List<string>>();
foreach (DataGridViewRow row in dataGridView.Rows)
{
List<string> rowSingle = new List<string>();
foreach (DataGridViewCell cell in row.Cells)
{
try
{
rowSingle.Add(cell.Value.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
rows.Add(rowSingle);
}
// AUDIT TABLE ( This way of doing things is not dynamic )
Page page = new Page(report);
page.SetLandscape();
int x = 10;
int y = 40;
int pastLength = 0;
foreach(string col in columns)
{
x += ((pastLength * 14) + 31);
page.Add(x, y, new RepString(fp_Title, col));
pastLength = col.Length;
}
page.Add(0, 52, new RepString(fp_Title, "_________________________________________________________________"));
/* Dynamic way starting
int rowX = 10;
int rowY = 105;
foreach (List<string> row in rows)
{
int pastLength2 = 0;
foreach (string rowItem in row)
{
page.Add(rowX, rowY, new RepString(fp_Word, rowItem));
rowX += ((pastLength * 14) + 31);
pastLength2 = rowItem.Length;
}
rowY += 30;
}
*/
fp_Title.rSizeMM = 8;
int amountRowsPerPageSoFar = 0;
int rowY = 80;
foreach (List<string> row in rows)
{
try
{
string iDItem = row[0];
page.Add(40, rowY, new RepString(fp_Word, iDItem));
string typeItem = row[1];
page.Add(120, rowY, new RepString(fp_Word, typeItem));
string descriptionItem = row[2];
page.Add(190, rowY, new RepString(fp_Word, descriptionItem));
string timestampItem = row[3];
page.Add(375, rowY, new RepString(fp_Word, timestampItem));
string userItem = row[4];
page.Add(555, rowY, new RepString(fp_Word, userItem));
string stationItem = row[5];
page.Add(655, rowY, new RepString(fp_Word, stationItem));
string activeItem = row[6];
page.Add(775, rowY, new RepString(fp_Word, activeItem));
amountRowsPerPageSoFar++;
rowY += 30;
if (amountRowsPerPageSoFar >= 17)
{
page = new Page(report);
page.SetLandscape();
amountRowsPerPageSoFar = 0;
rowY = 40;
}
}
catch (Exception ex)
{
}
}
RT.ViewPDF(report, "TestReport.pdf");
}
}
}

Remove all rows in duplication (different from distinct row selection)

How can I remove EVERY duplicating row in a DataTable, based on the value of two columns that are in duplication. Unfortunately, I am unable to find the equivalent LINQ Query. (I dont want distinct values even). The table below shall explain my problem
I want to delete every row in duplication based on Column_A and Column_B
COLUMN_A COLUMN_B COLUMN_C COLUMN_D.....
A B
C D
E F
G H
A B
E F
EXPECTED OUTPUT:
COLUMN_A COLUMN_B COLUMN_C COLUMN_D.....
C D
G H
Please help
var rowsToDelete = dataTable.AsEnumerable()
.GroupBy(r => new{A=r["COLUMN_A"],B=r["COLUMN_B"]})
.Where(g => g.Count() > 1)
.SelectMany(g=>g)
.ToList();
foreach (var row in rowsToDelete)
{
dataTable.Rows.Remove(row);
}
You can try with this sample
Link : http://geekswithblogs.net/ajohns/archive/2004/06/24/7191.aspx
Coding
private static void RemoveDuplicates(DataTable tbl,
DataColumn[] keyColumns)
{
int rowNdx = 0;
while(rowNdx < tbl.Rows.Count-1)
{
DataRow[] dups = FindDups(tbl, rowNdx, keyColumns);
if(dups.Length>0)
{
foreach(DataRow dup in dups)
{
tbl.Rows.Remove(dup);
}
}
else
{
rowNdx++;
}
}
}
private static DataRow[] FindDups(DataTable tbl,
int sourceNdx,
DataColumn[] keyColumns)
{
ArrayList retVal = new ArrayList();
DataRow sourceRow = tbl.Rows[sourceNdx];
for(int i=sourceNdx + 1; i<tbl.Rows.Count; i++)
{
DataRow targetRow = tbl.Rows[i];
if(IsDup(sourceRow, targetRow, keyColumns))
{
retVal.Add(targetRow);
}
}
return (DataRow[]) retVal.ToArray(typeof(DataRow));
}
private static bool IsDup(DataRow sourceRow,
DataRow targetRow,
DataColumn[] keyColumns)
{
bool retVal = true;
foreach(DataColumn column in keyColumns)
{
retVal = retVal && sourceRow[column].Equals(targetRow[column]);
if(!retVal) break;
}
return retVal;
}
Test
// create an example datatable with duplicate rows
DataTable tbl = new DataTable();
tbl.Columns.Add("ColumnA");
tbl.Columns.Add("ColumnB");
tbl.Columns.Add("ColumnC");
for(int i = 0; i<10; i++)
{
DataRow nr = tbl.NewRow();
nr["ColumnA"] = "A" + i.ToString();
nr["ColumnB"] = "B" + i.ToString();
nr["ColumnC"] = "C" + i.ToString();
tbl.Rows.Add(nr);
// duplicate
nr = tbl.NewRow();
nr["ColumnA"] = "A" + i.ToString();
nr["ColumnB"] = "B" + i.ToString();
nr["ColumnC"] = "C" + i.ToString();
tbl.Rows.Add(nr);
}
PrintRows(tbl); // show table with duplicates
//Create an array of DataColumns to compare
//If these columns all match we consider the
//rows duplicate.
DataColumn[] keyColumns =
new DataColumn[]{tbl.Columns["ColumnA"],
tbl.Columns["ColumnA"]};
//remove the duplicates
RemoveDuplicates(tbl, keyColumns);

Categories