I want to create a method which is able to take a Datatable and order the rows in descending order via multiple columns specified.
Below is an example of when I call the table sorting method:
t1 = getSortedTable(t1 , "Hotel Costs, Flight Cost DESC");
The Hotel Costs and Flight Costs are both type Double columns. The method for sorting is as below:
public static DataTable getSortedTable(DataTable dt, string sort)
{
DataTable newTable = new DataTable();
newTable = dt.Clone();
newTable.Rows.Clear();
DataRow[] newRows = dt.Select("", sort);
foreach (DataRow nr in newRows)
{
newTable.Rows.Add(nr.ItemArray);
}
return newTable;
}
The method only returns a table with rows sorted via the Flight Cost in descending order.
You can specify the DefaultView with the sort parameters and just return that view ToTable():
public static DataTable GetSortedTable(DataTable dt, string sort)
{
dt.DefaultView.Sort = sort;
return dt.DefaultView.ToTable();
}
EDIT: There's a chance that having spaces in your column names will interfere with the sort so you can either rename your column names like so:
foreach(var column in dt.Columns)
{
column.ColumnName = column.ColumnName.Replace(" ", "_");
}
Or use linq
if (dt.Rows.Count > 0)
{
dt = dt.AsEnumerable().OrderByDescending(x=>x.Field<decimal>("Hotel Costs")).ThenByDescending(x=>x.Field<decimal>("Flight Costs"))
.Select(x=>x)
.CopyToDataTable();
}
Related
I have DataGridview that I filtered some of it's row, I need to save the new datasource to a new DataTable, for some reason my current code don't work, here how I'm trying to convert it.
(LogGridView.DataSource as DataTable).DefaultView.RowFilter = string.Format("Type IN({0}) AND Date >= {1} AND Date <= {2} AND Content {3} '%{4}%'", typeFilter, startDate, endDate, likeQuery,keywordFilter.Text);
this.LogGridView.Sort(this.LogGridView.Columns[0], ListSortDirection.Ascending);
FilteredTable = LogGridView.DataSource as DataTable;
public DataTable FilteredTable
{
get;
set;
}
any Idea why it is not working
Thanks
What you have seen here is that after applying a filter and a sort, the sourced DataTable is unchanged though the DataGridView displays as expected. This is by design. So calling:
FilteredTable = LogGridView.DataSource as DataTable;
Just sets FilteredTable to the same as the original table.
Instead, we'll create a method to:
Create a new table with the same columns.
Select the rows from the original table using the same filter string and equivalent sort string as the DataGridView sort.
For each selected row, clone the items and add them as a new row into the new table.
Return the new table.
As seen here:
private DataTable CloneAlteredDataTableSource(DataGridView dgv)
{
DataTable dt = dgv.DataSource as DataTable;
if (dt == null)
{
return null;
}
DataTable clone = new DataTable();
foreach (DataColumn col in dt.Columns)
{
clone.Columns.Add(col.ColumnName, col.DataType);
}
string order = string.Empty;
switch (dgv.SortOrder)
{
case SortOrder.Ascending: order = "ASC"; break;
case SortOrder.Descending: order = "DESC"; break;
}
string sort = dgv.SortedColumn == null ? string.Empty : string.Format("{0} {1}", dgv.SortedColumn.Name, order);
DataRow[] rows = dt.Select(dt.DefaultView.RowFilter, sort);
foreach (DataRow row in rows)
{
object[] items = (object[])row.ItemArray.Clone();
clone.Rows.Add(items);
}
return clone;
}
And usage:
(this.LogGridView.DataSource as DataTable).DefaultView.RowFilter = string.Format("Type IN({0}) AND Date >= {1} AND Date <= {2} AND Content {3} '%{4}%'", typeFilter, startDate, endDate, likeQuery,keywordFilter.Text);
this.LogGridView.Sort(this.LogGridView.Columns[0], ListSortDirection.Ascending);
this.FilteredTable = this.CloneAlteredDataTableSource(this.LogGridView);
I have datatable on which I have to perform filter like where, order by. I have list of customerName. I want to filter data for each customername
I tried below code for same
foreach (string customer in CustName)
{
Datarow[] DataDR = TradeFinanceBF3.Select(TradeFinanceBF3.Columns["Cust_Name"].ColumnName.Trim() + "='A'", "USD equi DESC");
}
I get datarow, then how to pass it to dataTable, and how to pass all customer data to same datatable.
I tried LinkQuery Also to filter data as below
foreach (string customer in CustName)
{
DataTable selectedTable = TradeFinanceBF3.AsEnumerable()
.Where(r => r.Field<string>("Cust_Name") == customer)
.OrderByDescending(r => r.Field<double>("IndexABC"))
.CopyToDataTable();
///Datable OutPut= ?????
}
I got datatable, But then how to add all customer data to one datatable?
You could do something like this:
DataRow[] result = TradeFinanceBF3.Select("Cust_Name ='A'", "USD equi DESC");
DataTable aux = TradeFinanceBF3.Clone();
foreach (DataRow record in result)
{
aux.ImportRow(record);
}
I hope it will fix your issue
[Test]
public void GetCustomerData()
{
DataTable TradeFinanceBF3 = GetTable();
DataTable NewDatatable = TradeFinanceBF3.Clone();
IList<string> CustName = new List<string> { "Janet", "David" };
var selectedTable = (from dataRow in TradeFinanceBF3.AsEnumerable()
join customerName in CustName on dataRow.Field<string>("Cust_Name") equals customerName
select new
{
CustName = dataRow["Cust_Name"],
IndexABC = dataRow["IndexABC"]
}).OrderByDescending(p=>p.IndexABC);
foreach (var table in selectedTable)
{
NewDatatable.Rows.Add(table.CustName, table.IndexABC);
}
Console.Write(NewDatatable);
}
private DataTable GetTable()
{
// Here we create a DataTable with four columns.
DataTable table = new DataTable();
table.Columns.Add("Cust_Name", typeof(string));
table.Columns.Add("IndexABC", typeof(double));
// Here we add five DataRows.
table.Rows.Add("David", 1);
table.Rows.Add("Sam", 2);
table.Rows.Add("Christoff",2);
table.Rows.Add("Janet", 4);
table.Rows.Add("Melanie", 6);
return table;
}
I am trying to compare two datatables and capture the difference in third datatable.
DataTable one = new DataTable();
one.Columns.Add("ID");
one.Columns.Add("PCT");
one.Rows.Add("1", "0.1");
one.Rows.Add("2", "0.2");
one.Rows.Add("3", "0.3");
DataTable two = new DataTable();
two.Columns.Add("ID");
two.Columns.Add("PCT");
two.Columns.Add("OldPCT");
two.Rows.Add("1", "0.1", "0");
two.Rows.Add("2", "0.1", "0");
two.Rows.Add("3", "0.9", "0");
two.Columns.Remove("OldPCT");
//First method
DataTable three = two.AsEnumerable().Except(one.AsEnumerable()).CopyToDataTable();
foreach (DataRow dr in three.AsEnumerable())
{
string strID = dr[0].ToString();
string strPCT = dr[1].ToString();
}
//second method
var diffName = two.AsEnumerable().Select(r => r.Field<string>("PCT")).Except(one.AsEnumerable().Select(r => r.Field<string>("PCT")));
if (diffName.Any())
{
DataTable Table3 = (from row in two.AsEnumerable()
join name in diffName
on row.Field<string>("PCT") equals name
select row).CopyToDataTable();
}
So far I have tried two methods and I'm not not getting my expected result and it should be like.
In third datatable the values should be like mentioned below.
ID PCT
2 O.1
3 0.9
Recent One:
DataTable one = new DataTable();
one.Columns.Add("ID");
one.Columns.Add("PCT");
one.Rows.Add("1", "0.1");
one.Rows.Add("2", "0.2");
one.Rows.Add("2", "0.2");
one.Rows.Add("3", "0.3");
one.Rows.Add("3", "0.3");
DataTable two = new DataTable();
two.Columns.Add("ID");
two.Columns.Add("PCT");
two.Rows.Add("1", "0.1");
two.Rows.Add("2", "0.1");
two.Rows.Add("2", "0.1");
two.Rows.Add("3", "0.8");
two.Rows.Add("3", "0.9");
Now I need to get all the rows from datatable two except first row. But I am getting only last three rows.
Building on Hogan's answer, you can use DataRowComparer.Default as the second parameter to the Except() method (instead of creating a custom IEqualityComparer):
// this will get all rows from table two that don't match rows in one
// the result is an IEnumerable<DataRow>
var unmatched = two.AsEnumerable()
.Except(one.AsEnumerable(), DataRowComparer.Default);
// CopyToDataTable converts an IEnumerable<DataRow> into a DataTable
// but it blows up if the source object is empty
// this statement makes sure unmatched has data before calling CopyToDataTable()
// if it is empty, we 'clone' (make an empty copy) of one of the original DataTables
var three = unmatched.Any() ? unmatched.CopyToDataTable() : one.Clone();
This will do a value-based comparison of the fields in each row to determine if they are equal.
You need a custom IEqualityComparer
void Main()
{
DataTable one = new DataTable();
one.Columns.Add("ID");
one.Columns.Add("PCT");
one.Rows.Add("1", "0.1");
one.Rows.Add("2", "0.2");
one.Rows.Add("3", "0.3");
DataTable two = new DataTable();
two.Columns.Add("ID");
two.Columns.Add("PCT");
two.Columns.Add("OldPCT");
two.Rows.Add("1", "0.1", "0");
two.Rows.Add("2", "0.1", "0");
two.Rows.Add("3", "0.9", "0");
two.Columns.Remove("OldP
DataTable three = two.AsEnumerable().Except(one.AsEnumerable(),new RowEqualityComparer()).CopyToDataTable();
foreach (DataRow dr in three.AsEnumerable())
{
string strID = dr[0].ToString();
string strPCT = dr[1].ToString();
}
}
class RowEqualityComparer : IEqualityComparer<DataRow>
{
public bool Equals(DataRow b1, DataRow b2)
{
if ((b1.Field<string>("ID") == b2.Field<string>("ID")) && (b1.Field<string>("PCT") == b2.Field<string>("PCT")))
{
return true;
}
else
{
return false;
}
}
public int GetHashCode(DataRow bx)
{
return (bx.Field<string>("ID")+bx.Field<string>("PCT")).GetHashCode();
}
}
I have a DataTable that must be sorted, however i want to return a particular row as the last row even after sorting the DataTable. I will identify this row by a string value in a partiucular column.
public DataTable StandardReport3B(string missionId, string reportType, string startDate, string endDate)
{
List<long> missionIdList = new List<long>();
string[] missionIds = missionId.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string mission in missionIds)
{
missionIdList.Add(Convert.ToInt64(mission));
}
DataTable dt = new DataTable();
object reportData = null;
using (var ctx = new MedicalServiceEntities())
{
reportData = ctx.GetData(startDate, endDate, missionId).ToList();
}
dt = this.GenericListToDataTable(reportData);
DataView dv = dt.DefaultView;
dv.Sort = dt.Columns[0].ColumnName + " Asc";
return dv.ToTable();
}
You can use LINQ-to-DataSet:
dt = dt.AsEnumerable()
.OrderBy(r => r.Field<string>(0) == "Special Value")
.ThenBy(r => r.Field<string>(0))
.CopyToDataTable();
That works because the comparison returns either true or false and false is "lower".
In case someone just wants to know how to move the row in the table to another index as the title suggests: Add data row to datatable at predefined index
I will identify this row by a string value in a partiucular column.
What about:
dv.Sort = "ParticularColumn ASC, " + dt.Columns[0].ColumnName + " ASC">
Where "ParticularColumn" is a column with an identical, low value for all rows except the one you want to be last - and this row has a higher value.
I have two datatables.
1st table-----> DataTable _dtMain = new COrder().GetDetails();
2nd table-----> DataTable _dtSub = new CGrid().GetSubDetails();
I want to add the above two tables. I am using _dtMain.Merge(_dtSub );
But it will append _dtSub table to _dtMain table. I want datatable to add second table to first table in column wise (that means after first table last column)
Don't think there any built-in method for the thing you want to achieve.
I think you need to make your own implementation for that stuff, like for example an extension method
public static DataTable Aggregate(this DataTable dt1, DataTable dt2)
{
var aggregator = new DataTable();
//add columns from dt1 and dt2
//add rows from dt1 dt2
}
or, you can do a complete Outer Join on both tables, like descrived in this article.
After a lot of research , i find out the answer for my question that i posted before. Here is the code for combing 2 datatable & the data inside it.
public DataTable CombineTable(DataTable _dtGridDetails, DataTable _dtSubGridDetails)
{
//first create the datatable columns
DataSet mydataSet = new DataSet();
mydataSet.Tables.Add(" ");
DataTable myDataTable = mydataSet.Tables[0];
//add left table columns
DataColumn[] dcLeftTableColumns = new DataColumn[_dtGridDetails.Columns.Count];
_dtGridDetails.Columns.CopyTo(dcLeftTableColumns, 0);
foreach (DataColumn LeftTableColumn in dcLeftTableColumns)
{
if (!myDataTable.Columns.Contains(LeftTableColumn.ToString()))
myDataTable.Columns.Add(LeftTableColumn.ToString());
}
//now add right table columns
DataColumn[] dcRightTableColumns = new DataColumn[_dtSubGridDetails.Columns.Count];
_dtSubGridDetails.Columns.CopyTo(dcRightTableColumns, 0);
foreach (DataColumn RightTableColumn in dcRightTableColumns)
{
if (!myDataTable.Columns.Contains(RightTableColumn.ToString()))
{
// if (RightTableColumn.ToString() != RightPrimaryColumn)
myDataTable.Columns.Add(RightTableColumn.ToString());
}
}
//add left-table data to mytable
foreach (DataRow LeftTableDataRows in _dtGridDetails.Rows)
{
myDataTable.ImportRow(LeftTableDataRows);
}
for (int nIndex = 0; nIndex <= myDataTable.Rows.Count-1; nIndex++)
{
if (nIndex == _dtSubGridDetails.Rows.Count)
break;
myDataTable.Rows[nIndex][columnindex] = _dtSubGridDetails.Rows[nIndex][columnindex];
}
return myDataTable;
}