Change row position in DataTable or move row in DataTable - c#

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.

Related

c# - Save DataGridView After filter [duplicate]

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);

How to match data from two data tables have single row using LINQ or C# other way

I have two data-table like below getting data from other datatables
DataTable dt
DataTable dt2
Datatable dt have below value and every time dt and dt2 have single row values only then easy match them
F1 F2 F3 F4
Yes No Yes No
Data-table dt2 have below value
F1 F2 F3
Yes Yes Yes
I want to match output like below into Data-table dt3, both on column name
F1 F3
Yes Yes
See datatable dt and datable dt2 both have only one row every time, so you can match dynamic because both have one row only with column name and value, so I not am getting how to match with dynamic using LINQ or some other way for better understanding please check the below image
If without linq is an option then you can try the below method:
public DataTable getMatchedColumnAndValue(DataTable dt1, DataTable dt2)
{
try
{
var ndt = new DataTable();
//creating columns for the table
var dt1columns = dt1.Columns.Cast<DataColumn>().Select(s => s.ColumnName).ToList();
var dt2columns = dt2.Columns.Cast<DataColumn>().Select(s => s.ColumnName).ToList();
var MatchedCol = dt1columns.Intersect(dt2columns).ToList();
foreach (var col in MatchedCol)
{
ndt.Columns.Add(col);
}
//creating columsn matcehd row
var drnew = new string[MatchedCol.Count];
for (int i = 0; i < MatchedCol.Count; i++)
{
if (dt1.Rows[0][MatchedCol[i]].ToString() == dt2.Rows[0][MatchedCol[i]].ToString())
drnew[i] = dt1.Rows[0][MatchedCol[i]].ToString();
else
drnew[i] = null;
}
ndt.Rows.Add(drnew);
//removing null value columns
foreach (var col in MatchedCol)
{
if (ndt.AsEnumerable().All(dr => dr.IsNull(col)))
ndt.Columns.Remove(col);
}
return ndt;
}
catch (Exception ex)
{
throw ex;
}
}
I believe someone might able to do better but here is mine. ※
Simplified the answer a bit.
private static DataTable RegenerateDataTableInCommon(DataTable dt, DataTable dt2)
{
var dtRows = dt.Rows.Cast<DataRow>();
var dt2Rows = dt2.Rows.Cast<DataRow>();
var dtColumnNames = dt.Columns.Cast<DataColumn>().Select(dtC => dtC.ColumnName);
var columnNameMatched = dt2.Columns.Cast<DataColumn>().Select(dtC => dtC.ColumnName).Where(colName => dtColumnNames.Contains(colName));
var cellValueMatched = columnNameMatched.Where(colName => dtRows.First()[colName] == dt2Rows.First()[colName]);
DataTable outDt = new DataTable();
outDt.Clear();
DataRow nRow = outDt.NewRow();
foreach (var colName in cellValueMatched)
{
outDt.Columns.Add(colName);
nRow[colName] = dtRows.First()[colName];
}
outDt.Rows.Add(nRow);
return outDt;
}

c# - DataTable expression column using linq

I need to add columns from one datatable to another only if the columns in second datable not exists and also the second datatable has expression column that needs to set value from first datatable column using linq.
I have achieved this using foreach but how to do without foreach in linq ?
DataTable first = new DataTable();
first.Clear();
first.Columns.Add("Name");
first.Columns.Add("Exp");
DataRow _ra = dt1.NewRow();
_ra["Name"] = "Column_1";
_ra["Exp"] = "ExpTarget * Column_3";
first.Rows.Add(_ra);
DataRow _r = dt1.NewRow();
_r["Name"] = "Column_2";
_r["Exp"] = "ExpTarget * Column_3";
first.Rows.Add(_r);
DataRow _r2 = dt1.NewRow();
_r2["Name"] = "Column_3";
_r2["Exp"] = "ExpTarget";
first.Rows.Add(_r2);
DataTable second = new DataTable();
second.Clear();
second.Columns.Add("Column_3",typeof(System.Int16));
second.Columns.Add("ExpTarget",typeof(System.Int16));
DataRow _r21 = table.NewRow();
_r21["Column_3"] = 100;
_r21["ExpTarget"] = 2;
second.Rows.Add(_r21);
Code to add columns & expression is below, i need to avoid foreach linq below for expression column, how to do it without foreach ?
// get all columns from first datatable
string[] col = string.Join(",", first.AsEnumerable().Select(x => x["Name"].ToString()).ToArray()).Split(',').ToArray();
// get all columns and expression from first datatable
List<string> exp = string.Join(",", first.AsEnumerable().Select(x => x["Name"].ToString() + "~" + x["Exp"].ToString()).ToArray()).Split(',').ToList();
List<string[]> list = new List<string[]>();
list.Add(col);
second.Columns.AddRange(list.First().Select(r => second.Columns.Contains(r) ? second.Columns["Dummy"] : new DataColumn(r, typeof(System.Decimal))).ToArray());
exp.ForEach(r =>
{
second.Columns[r.ToString().Split('~')[0].ToString()].Expression = r.ToString().Split('~')[1].ToString();
});

compare datatables and save unmatched rows in third datatable

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();
}
}

dataview rowfilter value to datatable convertion

I want to convert dataview rowfilter value to datatable. I have a dataset with value. Now i was filter the value using dataview. Now i want to convert dataview filter values to datatable.please help me to copy it........
My partial code is here:
DataSet5TableAdapters.sp_getallempleaveTableAdapter TA = new DataSet5TableAdapters.sp_getallempleaveTableAdapter();
DataSet5.sp_getallempleaveDataTable DS = TA.GetData();
if (DS.Rows.Count > 0)
{
DataView datavw = new DataView();
datavw = DS.DefaultView;
datavw.RowFilter = "fldempid='" + txtempid.Text + "' and fldempname='" + txtempname.Text + "'";
if (datavw.Count > 0)
{
DT = datavw.Table; // i want to copy dataview row filter value to datatable
}
}
please help me...
You can use
if (datavw.Count > 0)
{
DT = datavw.ToTable(); // This will copy dataview's RowFilterd values to datatable
}
You can use DateView.ToTable() for converting the filtered dataview in to a datatable.
DataTable DTb = new DataTable();
DTb = SortView.ToTable();
The answer does not work for my situation because I have columns with expressions. DataView.ToTable() will only copy the values, not the expressions.
First I tried this:
//clone the source table
DataTable filtered = dt.Clone();
//fill the clone with the filtered rows
foreach (DataRowView drv in dt.DefaultView)
{
filtered.Rows.Add(drv.Row.ItemArray);
}
dt = filtered;
but that solution was very slow, even for just 1000 rows.
The solution that worked for me is:
//create a DataTable from the filtered DataView
DataTable filtered = dt.DefaultView.ToTable();
//loop through the columns of the source table and copy the expression to the new table
foreach (DataColumn dc in dt.Columns)
{
if (dc.Expression != "")
{
filtered.Columns[dc.ColumnName].Expression = dc.Expression;
}
}
dt = filtered;
The below code is for Row filter from Dataview and the result converted in DataTable
itemCondView.RowFilter = "DeliveryNumber = '1001'";
dataSet = new DataSet();
dataSet.Tables.Add(itemCondView.ToTable());

Categories