I have a DataTable. How to cut out its columns, and return a new DataTable?
I'm trying to create a method with the following parameters:
DataTable dt.
A string Array containing some fields.
It should look something like this:
public static DataTable cutDataTable(DataTable dt, List<string> fileds)
{
DataTable newDt = null;
// processing code...
return newDt
}
No exceptions.
You could make use of DataView.ToTable method for the purpose. For example,
var fields = new []{"Age","Id"}; //Fields to be selected.
var result = new DataView(table).ToTable(false, fields);
This would return a new DataTable from the source table with only the specified fields.
If I understand you correctly, you are trying to do something like this:
public static DataTable CutDataTable(DataTable dt, IList<string> columnsToRemove,
bool keepData = true)
{
DataTable newDt = (keepData ? dt.Copy() : dt.Clone());
foreach (string colName in columnsToRemove)
{
if (!newDt.Columns.Contains(colName)) continue;
newDt.Columns.Remove(colName);
}
return newDt;
}
Usage:
var dt = new DataTable();
dt.Columns.Add("Column1");
dt.Columns.Add("Column2");
dt.Columns.Add("Column3");
dt.Columns.Add("Column4");
dt.Rows.Add("1", "2", "3", "4");
var newDt = CutDataTable(dt, new[] { "Column3", "Column4" });
Console.WriteLine("New column count = " + newDt.Columns.Count);
Console.WriteLine("First row data: " + string.Join(",", newDt.Rows[0].ItemArray));
Output:
New column count = 2
First row data: 1,2
Related
I have a datatable where I need to take n number of columns. For ex: From the below datatable I need to take first 10 columns alone with data and put it in another datatable.
Code:
DataTable dtRecord = DAL.GetRecords();
I tried this and this doesn't take the required column.
var selectColumns = dtRecord .Columns.Cast<System.Data.DataColumn>().Take(10);
You can also use this
private DataTable GetNColumnsFromDataTable(DataTable tblSource, int outputCols)
{
DataTable columnOutput = tblSource.Copy();
if (outputCols > 0 && outputCols < tblSource.Columns.Count)
{
while (outputCols < columnOutput.Columns.Count)
{
columnOutput.Columns.RemoveAt(columnOutput.Columns.Count - 1);
}
}
return columnOutput;
}
You can do it like this:
var selectColumns = dtRecord.Columns.Cast<DataColumn>().Take(10);
var dtResult = new DataTable();
foreach (var column in selectColumns)
dtResult.Columns.Add(column.ColumnName);
foreach (DataRow row in dtRecord.Rows)
dtResult.Rows.Add(row.ItemArray.Take(10).ToArray());
Perhaps you should create a column of the same type and with the same expression:
dtResult.Columns.Add(column.ColumnName, column.DataType, column.Expression);
To copy from one DataTable to another, you can extract the columns of interest
var moveCols = dtRecord.Columns.Cast<DataColumn>().Take(10).Select(c => c.ColumnName).ToArray();
Then you must create new DataColumns in a new table, then create new DataRows in the new table:
var newTable = new DataTable();
foreach (var c in moveCols)
newTable.Columns.Add(c);
foreach (var r in dtRecord.AsEnumerable())
newTable.Rows.Add(moveCols.Select(c => r[c]).ToArray());
Which you can make an extension method on DataTable:
public static DataTable Slice(this DataTable dt, params string[] colnames) {
var newTable = new DataTable();
foreach (var c in colnames)
newTable.Columns.Add(c, dt.Columns[c].DataType);
foreach (var r in dt.AsEnumerable())
newTable.Rows.Add(colnames.Select(c => r[c]).ToArray());
return newTable;
}
Now you can call
var newTable = dtRecord.Slice(moveCols);
With a nice extension method, you can convert from Dictionarys to a DataTable dynamically:
var newTable = dtRecord.AsEnumerable().Select(r => moveCols.ToDictionary(c => c, c => r[c])).AsDataTable();
I have some for converting ExpandoObject and anonymous objects as well, as well as an extension to convert those to anonymous objects dynamically. Here is the code for Dictionarys to DataTable:
public static DataTable AsDataTable(this IEnumerable<IDictionary<string, object>> rows) {
var dt = new DataTable();
if (rows.Count() > 0) {
foreach (var kv in rows.First())
dt.Columns.Add(kv.Key, kv.Value.GetType());
foreach (var r in rows)
dt.Rows.Add(r.Values.ToArray());
}
return dt;
}
public static DataTable AsDataTable(this IEnumerable<Dictionary<string, object>> rows) => ((IEnumerable<IDictionary<string, object>>)rows).AsDataTable();
Get 10 columns:
var tbl = new System.Data.DataTable();
var cols = tbl.Columns.Cast<System.Data.DataColumn>().Take(10);
// if you wish to get first 10 columns...
If you want to get the data, then you have to loop through the columns to get the data.
var data = cols.SelectMany(x => tbl.Rows.Cast().Take(10).Select(y => y[x]));
of course, this will dump all the data into an ienumerable, if you want to use strong typed object or a list of one dimensional array, believe it's fairly simple, for example:
var data2 = cols.Select(x => tbl.Rows.Cast().Take(10).Select(y => y[x]).ToArray());
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'd like to use LINQ to take a datarow, and parse out the datacolumn names with their values.
So if I had a dataRow with the following columns and values:
DataColumn column1 with value '1'
DataColumn column2 with value 'ABC'
I'd like to have a string returned as "column1 = '1' and column2 = 'ABC'"
**** code should not care about the column names, nor the number of columns in the table.****
Purpose being, to filter a dataTable like:
var newRows = myTable.Select ("column1 = '1' and column2 = 'ABC'");
I can parse out the columns of the table like this:
string[] columnName = myTable.Columns.Cast<DataColumn>().Select(cn => cn.ColumnName).ToArray();
But I need to also extract values from a target row.
It feels like this might be a start:
{
string[] columnNames = existingTable.Columns.Cast<DataColumn>().Select(cn => cn.ColumnName).ToArray();
foreach (DataRow oldRow in existingTable.Rows)
{
var criteria = string.Join("and", columnNames, oldRow.ItemArray);
}
}
I think you are trying to get the column names and rows without actually referencing them. Is this what you're trying to do:
var table = new DataTable();
var column = new DataColumn {ColumnName = "column1"};
table.Columns.Add(column);
column = new DataColumn {ColumnName = "column2"};
table.Columns.Add(column);
var row = table.NewRow();
row["column1"] = "1";
row["column2"] = "ABC";
table.Rows.Add(row);
string output = "";
foreach (DataRow r in table.Rows)
{
output = table.Columns.Cast<DataColumn>()
.Aggregate(output, (current, c) => current +
string.Format("{0}='{1}' ", c.ColumnName, (string) r[c]));
output = output + Environment.NewLine;
}
// output should now contain "column1='1' column2='ABC'"
You can create extension methods out of this too, which operate both on a DataTable (for all rows) or a DataRow (for a single row):
public static class Extensions
{
public static string ToText(this DataRow dr)
{
string output = "";
output = dr.Table.Columns.Cast<DataColumn>()
.Aggregate(output, (current, c) => current +
string.Format("{0}='{1}'", c.ColumnName, (string)dr[c]));
return output;
}
public static string ToText(this DataTable table)
{
return table.Rows.Cast<DataRow>()
.Aggregate("", (current, dr) => current + dr.ToText() + Environment.NewLine);
}
}
I would highly recommend mapping to a class, and then adding another property which combines both of them!
See if this gets you in the right direction
EDIT Added solution using reflection since that is more in line with what you are wanting to accomplish
void Main()
{
List<MyClass> myColl = new List<MyClass>() { new MyClass() { myFirstProp = "1", mySecondProp = "ABC" } };
foreach (MyClass r in myColl)
{
List<string> rPropsAsStrings = new List<string>();
foreach (PropertyInfo propertyInfo in r.GetType().GetProperties())
{
rPropsAsStrings.Add(String.Format("{0} = {1}", propertyInfo.Name, propertyInfo.GetValue(r, null)));
}
Console.WriteLine(String.Join(" and ", rPropsAsStrings.ToArray()));
}
}
public class MyClass
{
public string myFirstProp { get; set; }
public string mySecondProp { get; set; }
}
The below uses Linq with strong typed properties
System.Data.DataTable table = new DataTable("ParentTable");
DataColumn column;
DataRow row;
column = new DataColumn();
column.DataType = typeof(string);
column.ColumnName = "column1";
table.Columns.Add(column);
column = new DataColumn();
column.DataType = typeof(string);
column.ColumnName = "column2";
table.Columns.Add(column);
row = table.NewRow();
row["column1"] = "1";
row["column2"] = "ABC";
table.Rows.Add(row);
var results = from myRow in table.AsEnumerable()
select String.Format("column1 = {0}, column2 = {1}", myRow[0], myRow[1]);
foreach (string r in results)
{
Console.WriteLine(r);
}
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.