How to pass DataTable.Select() result to a new DataTable? - c#

I have a DataTable named dt2 with data. I am calling its Select method to get some specific rows.
DataRow[] foundRows;
expression = "parent_id=1";
foundRows = dt2.Select(expression);
How can I pass the Select-method result to a new DataTable – say FilteredData?

You can use CopyToDataTable, available on IEnumerable<DataRow> types.
var filteredData = dt2.Select(expression).CopyToDataTable();

Just for clarity, the Select method returns an array of type DataRow. That's why we need to use CopyToDataTable(). Alex's answer is good. However, if the Select didn't return any rows, CopyToDataTable() will throw an InvalidOperationException.
So test that there is at least one DataRow before using the CopyToDataTable().
var filteredDataRows = dt2.Select(expression);
var filteredDataTable = new DataTable();
if(filteredDataRows.Length != 0)
filteredDataTable = filteredDataRows.CopyToDataTable();

Why not use a DataView instead?
DataView view = new DataView(dt2);
view.RowFilter = "parent_id = 1";
DataView will behave in very much the same way that a DataTable would with the added benefit that any change(s) to the underlying DataTable (dt2 in this case) would be automatically reflected in the DataView.

Related

Adding a column from one Datatable to another datatable of a dataset [duplicate]

How can I copy 1 data column from 1 data table to a new datatable. When I try to do it, I get the error Column 'XXX' already belongs to another DataTable.?
dataColumn = datatable1.Columns[1];
datatable2 = new DataTable();
datatable2.Columns.Add(dataColumn);
Thanks in Advance
You cannot copy DataColumns. What you'll need to do is create a new DataColumn in the new datatable with the same data type as in the old datatable's column, and then you need to run a FOR loop to bring in all the data from the old datatable to the new datatable.
See the following code. This assumes that the datatables have exactly the same number of rows.
DataTable dt1 = new DataTable();
DataTable dt2 = new DataTable();
dt2.Columns.Add("ColumnA", dt1.Columns["ColumnA"].DataType);
for (int i = 0; i < dt1.Rows.Count; i++)
{
dt2.Rows[i]["ColumnA"] = dt1.Rows[i]["ColumnA"];
}
Also, If the data you are copying are reference types and not value types you might want to see if a .Clone() method is available for the type, or make one yourself. Just doing 'this = that' in the FOR loop will not work on reference types.
You cannot copy a DataColumn. (DataColumns are very tightly coupled with their tables)
Instead, you can add a new column with the same name and datatype.
You might be looking for DataTable.Clone(), which will create a structual copy of an entire table. (With the same schema, but no data)
Just a thought, are your DataTables both in the same DataSet?
If so, you can create a named DataRelation between the columns of two tables (think foreign key).
Then you can add a Calculated DataColumn to your table that has its Expression property set to "Child(RelationName).ColumnName" or "Parent(RelationName).ColumnName" depending on the direction of the relationship.
This will give you the same effect as copying the column, but I believe it only evaluates it lazily. So maybe it will give you what you need.
There is an example here of how this works. The example uses the Sum aggregate function, but you just need to reference the column name and it will duplicate it in your DataTable
myDataSet.Relations.Add(
"Orders2OrderLines",
myDataSet.Tables["Orders"].Columns["OrderID"],
myDataSet.Tables["OrderLines"].Columns["OrderID"]);
ordersTable.Columns.Add("OrderTotal", typeof(decimal), "Sum(Child(Orders2OrderLines).ExtendedPrice)");
HTH
The problem is caused by the c# can not reuse the object instance created and uses it on multiples DataTables. For this it is necessary to create a new object DataCollumn for each loop iteration.
foreach (DataTable table in DATASET.Tables)
{
DataColumn yourDataCollumn = new DataColumn("Name of DataCollumn", typeof(Your data type));
// your logic here
}
Hope it's help...
I used the below to merge two tables using mostly LINQ and only looping through the rows at the end. I wouldn't call it pretty but it does work. Using the join to prevent some of the assumptions listed above.
DataTable tableOne = getTableOne();
DataTable tableTwo = getTableTwo();
var oneColumns = tableOne.Columns.Cast<DataColumn>()
.Select(p => new Column(p.ColumnName, DataType))
.ToArray();
var twoColumns = tableTwo.Columns.Cast<DataColumn>()
.Select(p => new DataColumn(p.ColumnName, p.DataType))
.ToArray();
var matches = (from a in tableOne.AsEnumerable()
join b in tableTwo.AsEnumerable() on a["column_name"] equals b["column_name"]
select a.ItemArray.Concat(b.ItemArray)).ToArray();
DataTable merged = new DataTable();
merged.Columns.AddRange(oneColumns);
merged.Columns.AddRange(twoColumns);
foreach (var m in matches) { merged.Rows.Add(m.ToArray()); }
No looping required , Refer this , Hope this should solve your problem...
DataTable dt = new DataTable();
//fill the dt here
DataTable dt2 = new DataTable();
string[] strCols = {"Column Name to copy"};
dt2 = dt.DefaultView.ToTable("newTableName", false, strCols);

Linq on DataTable: select specific column into datatable, not whole table

I'm running a LINQ query on a datatable in c#.
I want to select specific columns rather than the whole row and enter the result into a datatable. How can i do that??
My Code:
public DataTable getConversions(string c_to, string p_to)
{
var query = from r in matrix.AsEnumerable()
where r.Field<string>("c_to") == c_to &&
r.Field<string>("p_to") == p_to
select r;
DataTable conversions = query.CopyToDataTable();
If you already know beforehand how many columns your new DataTable would have, you can do something like this:
DataTable matrix = ... // get matrix values from db
DataTable newDataTable = new DataTable();
newDataTable.Columns.Add("c_to", typeof(string));
newDataTable.Columns.Add("p_to", typeof(string));
var query = from r in matrix.AsEnumerable()
where r.Field<string>("c_to") == "foo" &&
r.Field<string>("p_to") == "bar"
let objectArray = new object[]
{
r.Field<string>("c_to"), r.Field<string>("p_to")
}
select objectArray;
foreach (var array in query)
{
newDataTable.Rows.Add(array);
}
Try Access DataTable easiest way which can help you for getting perfect idea for accessing DataTable, DataSet using Linq...
Consider following example, suppose we have DataTable like below.
DataTable ObjDt = new DataTable("List");
ObjDt.Columns.Add("WorkName", typeof(string));
ObjDt.Columns.Add("Price", typeof(decimal));
ObjDt.Columns.Add("Area", typeof(string));
ObjDt.Columns.Add("Quantity",typeof(int));
ObjDt.Columns.Add("Breath",typeof(decimal));
ObjDt.Columns.Add("Length",typeof(decimal));
Here above is the code for DatTable, here we assume that there are some data are available in this DataTable, and we have to bind Grid view of particular by processing some data as shown below.
Area | Quantity | Breath | Length | Price = Quantity * breath *Length
Than we have to fire following query which will give us exact result as we want.
var data = ObjDt.AsEnumerable().Select
(r => new
{
Area = r.Field<string>("Area"),
Que = r.Field<int>("Quantity"),
Breath = r.Field<decimal>("Breath"),
Length = r.Field<decimal>("Length"),
totLen = r.Field<int>("Quantity") * (r.Field<decimal>("Breath") * r.Field<decimal>("Length"))
}).ToList();
We just have to assign this data variable as Data Source.
By using this simple Linq query we can get all our accepts, and also we can perform all other LINQ queries with this…
Here I get only three specific columns from mainDataTable and use the filter
DataTable checkedParams = mainDataTable.Select("checked = true").CopyToDataTable()
.DefaultView.ToTable(false, "lagerID", "reservePeriod", "discount");
LINQ is very effective and easy to use on Lists rather than DataTable. I can see the above answers have a loop(for, foreach), which I will not prefer.
So the best thing to select a perticular column from a DataTable is just use a DataView to filter the column and use it as you want.
Find it here how to do this.
DataView dtView = new DataView(dtYourDataTable);
DataTable dtTableWithOneColumn= dtView .ToTable(true, "ColumnA");
Now the DataTable dtTableWithOneColumn contains only one column(ColumnA).
Your select statement is returning a sequence of anonymous type , not a sequence of DataRows. CopyToDataTable() is only available on IEnumerable<T> where T is or derives from DataRow. You can select r the row object to call CopyToDataTable on it.
var query = from r in matrix.AsEnumerable()
where r.Field<string>("c_to") == c_to &&
r.Field<string>("p_to") == p_to
select r;
DataTable conversions = query.CopyToDataTable();
You can also implement CopyToDataTable Where the Generic Type T Is Not a DataRow.

DataSet sorting

In DataTable I could sorting with
dataTable.DefaultView.Sort = "SortField DESC";
I'm getting a DataSet from database, I was wondering could I do a sorting on the DataSet like how I do it in DataTable.
you can still access the DataTable from the the data set as follows,
ds.Tables[0].DefaultView.Sort =" criterian";
Hope this helps.
DataView view = ds.Tables[0].DefaultView;
view.Sort = "SortField DESC";
http://msdn.microsoft.com/en-us/library/1ay5y4w0(v=vs.71).aspx
http://social.msdn.microsoft.com/Forums/nl/netfxbcl/thread/adbd95cd-49d1-483d-b2b2-4b696a66e9a6
Access the DataTable from the the DataSet as follows,
ds.Tables[0].DefaultView.Sort = "SortField DESC";
Hope this helps.
For advanced sorting needs you might want to use LINQ like described here. Basically it allows creating a DataView from a LINQ query using the System.Data.DataTableExtensions.AsDataFiew extension method.
Alternatively if you are OK with (or maybe even prefer) using an IEnumerable
you could use the System.Data.DataTableExtensions.AsEnumerable extension method. For example:
var enumerable = dataSet.Tables[0].AsEnumerable()
.OrderBy(x => x.Field<string>("ColumnName")
.ThenByDescending(x => x.Field<int?>("OtherColumnName")??0);
From tha DataSet object, you can access all the DataTable to intract with.
Try this:
DataDet.Tables[0].DefaultView.Sort = "sort criteria";
DataSet fileTransferDetail = null;//Data to be sorted.
DataSet result = null;//Declare a dataSet to be filled.
//Sort data.
fileTransferDetail.Tables[0].DefaultView.Sort = "ID DESC";
//Store in new Dataset
result.Tables.Add(fileTransferDetail.Tables[0].DefaultView.ToTable());
Try the following code.
DataView dv = new DataView();
dv = ds.Tables[0].DefaultView;
dv.Sort=value;

SELECT DISTINCT in DataView's RowFilter

I'm trying to narrow down the rows that are in my DataView based on a relation with another table, and the RowFilter I'm using is as follows;
dv = new DataView(myDS.myTable,
"id IN (SELECT DISTINCT parentID FROM myOtherTable)",
"name asc",
DataViewRowState.CurrentRows);
"myTable" and "myOther" table are related via myTable.ID and myOtherTable.parentID, and so the idea is that the DataView should only contain rows from "myTable" which have corresponding child rows in "myOtherTable".
Unfortunately, I'm getting this error;
Syntax error: Missing operand after
'DISTINCT' operator.
The SQL is fine as far as I am aware, so I'm wondering is there some limitation on using the DISTINCT keyword as part of RowFilter's SQL? Anyone have any idea?
Unfortunately, I don't think you can perform a subquery in a DataView's filter expression. You're only allowed to use a subset of SQL in some expressions (documented here).
You'll probably need to perform your subquery (SELECT DISTINCT parentID FROM myOtherTable) separately.
This article describes the problem and a possible solution.
Unfortunately you can't do it that way, as the RowFilter property does not support the distinct keyword. Here is the list of expressions you can perform in a RowFilter (which is just a DataColumn Expression): http://msdn.microsoft.com/en-us/library/system.data.datacolumn.expression.aspx
DataViews have a ToTable method, and several overloads take a boolean to specify whether to return only the distinct rows.
Here is one method: http://msdn.microsoft.com/en-us/library/wec2b2e6.aspx
Here is how you would use it:
DataTable newDataTable = myDataView.ToTable( true, [array of column names as strings] );
DataView dvBindAssignedByDropDown = new DataView();
DataTable dtBindAssignedByDropDown = new DataTable();
dvBindAssignedByDropDown = ds.Tables[0].DefaultView;
string[] strColnames=new string[2];
strColnames[0] = "RedNames";
strColnames[1] = "RedValues";
dtBindAssignedByDropDown = dvBindAssignedByDropDown.ToTable(true, strColnames);
ddlAssignedby.DataTextField = "RedNamesNames";
ddlAssignedby.DataValueField = "RedNames";
ddlAssignedby.DataSource = dtBindAssignedByDropDown;
ddlAssignedby.DataBind();
ddlAssignedby.Items.Insert(0, "Assigned By");
ddlAssignedby.Items[0].Value = "0";
the following code is extracting distinct values/records from a table/dataview, namely(PROD_DESP_TRN) having field(CONTAINER_NO)
Finally, this code is filling a combobox(cmbContainerNo) with unique values/records
Form Level Declaration:
Dim dsLocal As DataSet
Dim dvm As DataViewManager
Private Sub FillcomboContainer()
Try
Dim dv As DataView = New DataView
cmbContainerNo.DataSource = Nothing
dv = dvm.CreateDataView(dsLocal.Tables("PROD_DESP_TRN"))
dv.Sort = "CONTAINER_NO"
cmbContainerNo.DataSource = dv.ToTable(True, "CONTAINER_NO")
cmbContainerNo.DisplayMember = "CONTAINER_NO"
Catch ex As Exception
MsgBox(ex.Message)
Finally
End Try
End Sub
Try just leaving out the "DISTINCT". In this case, the results should be the same with or without. Troubleshoot from there.

LINQ query on a DataTable

I'm trying to perform a LINQ query on a DataTable object and bizarrely I am finding that performing such queries on DataTables is not straightforward. For example:
var results = from myRow in myDataTable
where results.Field("RowNo") == 1
select results;
This is not allowed. How do I get something like this working?
I'm amazed that LINQ queries are not allowed on DataTables!
You can't query against the DataTable's Rows collection, since DataRowCollection doesn't implement IEnumerable<T>. You need to use the AsEnumerable() extension for DataTable. Like so:
var results = from myRow in myDataTable.AsEnumerable()
where myRow.Field<int>("RowNo") == 1
select myRow;
And as #Keith says, you'll need to add a reference to System.Data.DataSetExtensions
AsEnumerable() returns IEnumerable<DataRow>. If you need to convert IEnumerable<DataRow> to a DataTable, use the CopyToDataTable() extension.
Below is query with Lambda Expression,
var result = myDataTable
.AsEnumerable()
.Where(myRow => myRow.Field<int>("RowNo") == 1);
var results = from DataRow myRow in myDataTable.Rows
where (int)myRow["RowNo"] == 1
select myRow
It's not that they were deliberately not allowed on DataTables, it's just that DataTables pre-date the IQueryable and generic IEnumerable constructs on which Linq queries can be performed.
Both interfaces require some sort type-safety validation. DataTables are not strongly typed. This is the same reason why people can't query against an ArrayList, for example.
For Linq to work you need to map your results against type-safe objects and query against that instead.
As #ch00k said:
using System.Data; //needed for the extension methods to work
...
var results =
from myRow in myDataTable.Rows
where myRow.Field<int>("RowNo") == 1
select myRow; //select the thing you want, not the collection
You also need to add a project reference to System.Data.DataSetExtensions
I realize this has been answered a few times over, but just to offer another approach:
I like to use the .Cast<T>() method, it helps me maintain sanity in seeing the explicit type defined and deep down I think .AsEnumerable() calls it anyways:
var results = from myRow in myDataTable.Rows.Cast<DataRow>()
where myRow.Field<int>("RowNo") == 1 select myRow;
or
var results = myDataTable.Rows.Cast<DataRow>()
.FirstOrDefault(x => x.Field<int>("RowNo") == 1);
As noted in comments, does not require System.Data.DataSetExtensions or any other assemblies (Reference)
var query = from p in dt.AsEnumerable()
where p.Field<string>("code") == this.txtCat.Text
select new
{
name = p.Field<string>("name"),
age= p.Field<int>("age")
};
the name and age fields are now part of the query object and can be accessed like so: Console.WriteLine(query.name);
Using LINQ to manipulate data in DataSet/DataTable
var results = from myRow in tblCurrentStock.AsEnumerable()
where myRow.Field<string>("item_name").ToUpper().StartsWith(tbSearchItem.Text.ToUpper())
select myRow;
DataView view = results.AsDataView();
//Create DataTable
DataTable dt= new DataTable();
dt.Columns.AddRange(new DataColumn[]
{
new DataColumn("ID",typeof(System.Int32)),
new DataColumn("Name",typeof(System.String))
});
//Fill with data
dt.Rows.Add(new Object[]{1,"Test1"});
dt.Rows.Add(new Object[]{2,"Test2"});
//Now Query DataTable with linq
//To work with linq it should required our source implement IEnumerable interface.
//But DataTable not Implement IEnumerable interface
//So we call DataTable Extension method i.e AsEnumerable() this will return EnumerableRowCollection<DataRow>
// Now Query DataTable to find Row whoes ID=1
DataRow drow = dt.AsEnumerable().Where(p=>p.Field<Int32>(0)==1).FirstOrDefault();
//
Try this simple line of query:
var result=myDataTable.AsEnumerable().Where(myRow => myRow.Field<int>("RowNo") == 1);
You can use LINQ to objects on the Rows collection, like so:
var results = from myRow in myDataTable.Rows where myRow.Field("RowNo") == 1 select myRow;
This is a simple way that works for me and uses lambda expressions:
var results = myDataTable.Select("").FirstOrDefault(x => (int)x["RowNo"] == 1)
Then if you want a particular value:
if(results != null)
var foo = results["ColName"].ToString()
Try this
var row = (from result in dt.AsEnumerable().OrderBy( result => Guid.NewGuid()) select result).Take(3) ;
Most likely, the classes for the DataSet, DataTable and DataRow are already defined in the solution. If that's the case you won't need the DataSetExtensions reference.
Ex. DataSet class name-> CustomSet, DataRow class name-> CustomTableRow (with defined columns: RowNo, ...)
var result = from myRow in myDataTable.Rows.OfType<CustomSet.CustomTableRow>()
where myRow.RowNo == 1
select myRow;
Or (as I prefer)
var result = myDataTable.Rows.OfType<CustomSet.CustomTableRow>().Where(myRow => myRow.RowNo);
var results = from myRow in myDataTable
where results.Field<Int32>("RowNo") == 1
select results;
In my application I found that using LINQ to Datasets with the AsEnumerable() extension for DataTable as suggested in the answer was extremely slow. If you're interested in optimizing for speed, use James Newtonking's Json.Net library (http://james.newtonking.com/json/help/index.html)
// Serialize the DataTable to a json string
string serializedTable = JsonConvert.SerializeObject(myDataTable);
Jarray dataRows = Jarray.Parse(serializedTable);
// Run the LINQ query
List<JToken> results = (from row in dataRows
where (int) row["ans_key"] == 42
select row).ToList();
// If you need the results to be in a DataTable
string jsonResults = JsonConvert.SerializeObject(results);
DataTable resultsTable = JsonConvert.DeserializeObject<DataTable>(jsonResults);
Example on how to achieve this provided below:
DataSet dataSet = new DataSet(); //Create a dataset
dataSet = _DataEntryDataLayer.ReadResults(); //Call to the dataLayer to return the data
//LINQ query on a DataTable
var dataList = dataSet.Tables["DataTable"]
.AsEnumerable()
.Select(i => new
{
ID = i["ID"],
Name = i["Name"]
}).ToList();
For VB.NET The code will look like this:
Dim results = From myRow In myDataTable
Where myRow.Field(Of Int32)("RowNo") = 1 Select myRow
IEnumerable<string> result = from myRow in dataTableResult.AsEnumerable()
select myRow["server"].ToString() ;
Try this...
SqlCommand cmd = new SqlCommand( "Select * from Employee",con);
SqlDataReader dr = cmd.ExecuteReader( );
DataTable dt = new DataTable( "Employee" );
dt.Load( dr );
var Data = dt.AsEnumerable( );
var names = from emp in Data select emp.Field<String>( dt.Columns[1] );
foreach( var name in names )
{
Console.WriteLine( name );
}
You can get it work elegant via linq like this:
from prod in TenMostExpensiveProducts().Tables[0].AsEnumerable()
where prod.Field<decimal>("UnitPrice") > 62.500M
select prod
Or like dynamic linq this (AsDynamic is called directly on DataSet):
TenMostExpensiveProducts().AsDynamic().Where (x => x.UnitPrice > 62.500M)
I prefer the last approach while is is the most flexible.
P.S.: Don't forget to connect System.Data.DataSetExtensions.dll reference
you can try this, but you must be sure the type of values for each Column
List<MyClass> result = myDataTable.AsEnumerable().Select(x=> new MyClass(){
Property1 = (string)x.Field<string>("ColumnName1"),
Property2 = (int)x.Field<int>("ColumnName2"),
Property3 = (bool)x.Field<bool>("ColumnName3"),
});
I propose following solution:
DataView view = new DataView(myDataTable);
view.RowFilter = "RowNo = 1";
DataTable results = view.ToTable(true);
Looking at the DataView Documentation, the first thing we can see is this:
Represents a databindable, customized view of a DataTable for sorting, filtering, searching, editing, and navigation.
What I am getting from this is that DataTable is meant to only store data and DataView is there enable us to "query" against the DataTable.
Here is how this works in this particular case:
You try to implement the SQL Statement
SELECT *
FROM myDataTable
WHERE RowNo = 1
in "DataTable language". In C# we would read it like this:
FROM myDataTable
WHERE RowNo = 1
SELECT *
which looks in C# like this:
DataView view = new DataView(myDataTable); //FROM myDataTable
view.RowFilter = "RowNo = 1"; //WHERE RowNo = 1
DataTable results = view.ToTable(true); //SELECT *

Categories