I have a datatable dt with a single column and a list of strings. If I want to change this to a list (for example, to make it easier to compare with another list) I would default to something like:
var ls = new List<string>();
foreach (DataRow dr in dt.Rows)
{
ls.Add(dr["COLUMN_NAME"].ToString());
}
I don't think it's possible to get more efficient than that, but is there shorter way to write this, maybe using LINQ?
As mentioned in the comment, the LINQ way might be shorter in code, but not more efficient.
Anyway, here is the one liner LINQ version.
var list = new List<string>(dt.Rows.Cast<DataRow>().Select(r => r["COLUMN_NAME"].ToString()));
Related
I want to check the rows of two data tables. If there is an exact match I want to remove all but one.
I've figured out how to compare two rows of data. I'm not sure the best way to return the cleaned up version without duplicates.
the tables within my program are pulling tables from a database, so i simplified them for the example.
Here's what I've worked out so far.
var table1 = new list<string>();
var table2 = new list<string>();
foreach (DataRow row1 in table.Rows)
foreach (DataRow row2 in table2.Rows)
{
var array1 = row1.ItemArray;
var array2 = row2.ItemArray;
if (array1.SequenceEqual(array2))
{
// store the unique elements within a new list?
// remove duplicates and return the remainder?
}
}
I thought using the Intersect() method might be an option as well.
Cast to a hashset of your desired type. They will automatically remove duplicates, as by definition hashset cannot have duplicate entries.
More info:
https://www.dotnetperls.com/hashset
I have a datatable which I have filterd using Linq:
result = myDataTable.AsEnumerable().Where(row => row.Field<string>("id").Contains(values));
I have also tried using CopyToDataTable methods like
result.CopyToDataTable()
and
result.CopyToDataTable<DataRow>()
but they didn't work.
How can I convert result to new DataTable?
I have search many Stack Overflow questions and many other tutorials but I can't find what I want.
UPDATE
I have concatenated HashSet to comma separated values, I think I should use array of String or HashSet?
I suggest you create object of DataTable and import row in it by calling ImportRow() function , that will resolve issue.
DataTable.ImportRow Method
Example code.
DataTable tblClone = datTab.Clone();
foreach (DataRow datRow in datTab.Rows)
{
tblClone.ImportRow(datRow);
}
for you it will be like
var result = myDataTable.AsEnumerable().Where(row => row.Field<string>("id").Contains(values));
DataTable tblClone = myDataTable.Clone();
foreach(DataRow dr in result)
tblClone.ImportRow(dr);
.CopyToDataTable<DataRow>() returns a DataTable, it will not modify the variable unless you re-assign it.
result = myDataTable.AsEnumerable().Where(row => row.Field<string>("id").Contains(values));
Then you actually need a DataTable object.
DataTable resultDT = result.CopyToDataTable<DataRow>();
Edit: As Tim pointed out, if no rows are returned by your query, an exception will be thrown "The source contains no DataRows"
You could do something like so;
DataTable resultDT = result.Any() ? result.CopyToDataTable<DataRow>() : myDataTable.Clone();
But that will run the query twice (also as Tim pointed out).
Therefore you could convert that to a list object using (.ToList()), check the count and do your processing then. That has performance implications in such that you create a new instance of the object (List object).
Doing a try/catch with attempt to convert it to DataTable also isn't a good idea. See Pranays answer for another great way to achieve the final result.
I have a small problem, which I just cannot find how to fix it.
For my data table, Dictionar.DtDomenii, I need to copy the unique data from my other data table, Dictionar.Dt.
I wrote a query, but when using query.CopyToDataTable() to copy the data into my DtDomenii table, the "CopyToDataTable" function does not show...
Am I doing something wrong? Is there an easier way to copy distinct data (categories from my example) from one data table to another?
PS: I've already read the information from MSDN https://msdn.microsoft.com/en-us/library/bb386921%28v=vs.110%29.aspx
void LoadCategories()
{
var query = (from cat in Dictionar.dt.AsEnumerable()
select new
{
categorie = categorii.Field<string>("Categoria")
}).Distinct();
// This statement does not work:
Dictionar.dtDomenii = query.CopyToDataTable();
}
Only collections of DataRows can use the CopyToDataTable method. For example:
DataTable table = new DataTable();
table.AsEnumerable().CopyToDataTable(); // this works
List<DataRow> dataRows = new List<DataRow>();
dataRows.CopyToDataTable(); // this also works
List<string> strings = new List<string>();
strings.CopyToDataTable(); // this does not work
The select new... part of your query is converting the DataRows into objects. You need to convert the objects back into DataRows before you can use the CopyToDataTable method.
You might have better luck doing something like this:
DataTable copy = Dictionar.dt
.AsEnumerable() // now an enumerable of DataRow
.Distinct() // remove duplicates, still an enumerable of DataRow
.CopyToDataTable(); // done!
You can also make a complete copy of the table with Dictionar.dt.Copy(), then remove the duplicate rows manually.
I'm new to LINQ and doing some experiments with it.
Sorry if it is a duplicate but I cant seem to find proper guide (for me) to it
I want to replace this code :
DataTable table
List<string> header = new List<string>();
table.Columns.Cast<DataColumn>().ToList().ForEach(col => header.Add(col.ColumnName));
with something LINQ like:
var LINQheader = from mycol in table.Columns select mycol.ColumnName;
LINQheader.tolist();
but it doesn't even compile.
what I want Is not a one line solution but would like some logic to understand how construct it with more complicated environments (Like choosing many node in XML with some logic)
here is the original code
table.Columns.Cast<DataColumn>().ToList().ForEach(col => header.Add(col.ColumnName));
Why Cast used?
because it allows you to treat DataColumnCollection items as a DataColumn not an object.
Why ToList used?
becuase it converts your IEnumerable to List and allows you to call ForEach because this function is special method that exists in List class.
Why ForEach used?
because it allows you to do what you want for each element on the list (in your case it adds column name of each column to another list(header)).
Simplified version:
now assume you want to add column names to header where they starts with "Student"
you can write something like this
DataTable table = new DataTable();
List<string> header = new List<string>();
foreach (DataColumn col in table.Columns)
{
if (col.ColumnName.StartsWith("Id")) // you can remove this line if you want to add all of them
header.Add(col.ColumnName);
}
you can also use this
table.Columns.Cast<DataColumn>()
.ToList()
.ForEach(col =>
{
if (col.ColumnName.StartsWith("Id"))
header.Add(col.ColumnName)
});
or
var headers = table.Columns.Cast<DataColumn>()
.Where(col => col.ColumnName.StartsWith("Id"))
.Select(col => col.ColumnName);
header.AddRange(headers);
You can use Enumerable.Aggregate() for this:
var header = table.Columns.Cast<DataColumn>().Aggregate(new List<string>(), (list, col) => { list.Add(col.ColumnName); return list; });
In general, Linq allows for retrieval and transformation of sequences of data from data sources. What you want to do in this question is to iterate over a sequence and return an immediate result. That isn't the primary focus of Linq, but there are methods that perform tasks like this, including Aggregate(), Average(), Count(), Max() and so on.
var LINQheader = from mycol in table.column select mycol.ColumnName;
LINQheader.tolist();
This will not compile as there is no such property in DataTable as column, there is only Columns, and you have to use .Cast() method as they are not implementing right interface (see #Uriil's answer).
Try this:
var LINQheader = from mycol in table.Columns.Cast<DataColumn>()
select mycol.ColumnName;
LINQheader.tolist();
If you want to use wrap it in an extension method, you can do it like this:
public static IEnumerable<string> GetHeaderColumns (this DataTable dataTable)
{
if (dataTable == null || !dataTable.Columns.Any())
{
yield break;
}
foreach (var col in dataTable.Columns.Cast<DataColumn>())
{
yield return col.ColumnName;
}
}
static void Main(string[] args)
{
DataTable tbl = new DataTable();
tbl.Columns.Add("A");
tbl.Columns.Add("B");
var p = from DataColumn col in tbl.Columns select col.ColumnName;
foreach(string a in p)
{
Console.WriteLine(a);
}
}
Here little code example. If you want to be List<string>, use ToList().
EDIT:
Like #Grundy says you missing to specify type of the col, which is DataColumn.
List<string> columnList = (from DataColumn mycol in table.Columns select mycol.ColumnName).ToList();
Here this will be your one line.
Why not simple select like
DataTable table;
IEnumerable<string> header = from mycol in table.Columns.Cast<DataColumn>()
select mycol.ColumnName;
You have some problems, which requires workaround:
ForEach is List specifict method, so can can not translate it into LINQ
LINQ is for data selection, aggregation, but not for data
modification
table.Columns, returns DataColumnCollection, which does not
implement IEnumerable<T>, so you will have to cast it anyway:
var LINQheader = from mycol in table.Columns.Cast<DataColumn>()
select name.ColumnName;
I am fairly new to LINQ and am trying to apply this query on a datatable called "EmpInfo" to sort it.
var sortedRows = (from myRow in EmpInfoDS.Tables["EmpInfo"].AsEnumerable()
orderby myRow["EmpID"] ascending
select myRow).ToArray();
This works. The next thing I am trying to do is to copy the results into the SAME datatable.
EmpInfoDS.Tables["EmpInfo"].Clear();
EmpInfoDS.Tables["EmpInfo"] = sortedRows.CopyToDataTable();
The second line throws the following error:
"Property or indexer 'System.Data.DataTableCollection.this[string]'
cannot be assigned to -- it is read only"
Please some one tell me how to deal with this. And if there is another way please tell me.
The error says that you can't assign the table using indexers because Tables is a readonly property. So to solve the problem:
EmpInfoDS.Tables.Remove("EmpInfo");
DataTable dt = sortedRows.CopyToDataTable();
dt.TableName = "EmpInfo";
EmpInfoDS.Tables.Add(dt);
I think this is how it can be done: (you might prefer a different LoadOption)
EmpInfoDS.Tables["EmpInfo"].Clear();
sortedRows.CopyToDataTable(EmpInfoDS.Tables["EmpInfo"], LoadOption.OverwriteChanges);
This should also work:
EmpInfoDS.Tables["EmpInfo"].Clear();
foreach (var row in sortedRows)
EmpInfoDS.Tables["EmpInfo"].Rows.Add(row);
With LINQ, you have to watch out for deferred execution. Basically, queries are not executed until the data from the query is consumed. If you want a query to execute immediately, use the ToList() method.
var sortedRows = EmpInfoDS.EmpInfo.OrderBy(e => e.EmpID).ToList();
Next, the code is straight forward:
var employees = EmpInfoDS.EmpInfo;
employees.Clear();
foreach (var sortedRow in sortedRows)
employees.Add(sortedRow);
EmpInfoDS.SaveChanges();