Compare DataRow collection to List<T> - c#

I have a List<string> and I have a DataTable.
One of the columns in a DataRow is ID. The List holds instances of this ID.
The DataTable gets populated on a Timer.
I want to return items from the List that are not in the DataTable into another list.

You will want to do something like this
var tableIds = table.Rows.Cast<DataRow>().Select(row => row["ID"].ToString());
var listIds = new List<string> {"1", "2", "3"};
return listIds.Except(tableIds).ToList();
You can cast the rows in the data table to be an IEnumerable collection and then select the "ID" column value from each of them. You can then use the Enumerable.Except extension method to get all of the values from the List that are not in the collection you just made.
If you need to get the values that are in the table but not the list, just reverse listIds and tableIds.

If your table was something like that:
DataTable dt = new DataTable();
dt.Columns.Add("ID");
DataRow dr = dt.NewRow();
dt.PrimaryKey = new DataColumn[] {dt.Columns[0]};
dr["ID"] = "1";
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["ID"] = "2";
dt.Rows.Add(dr);
dr = dt.NewRow();
dr["ID"] = "3";
dt.Rows.Add(dr);
and the list was something like this:
List<string> ls = new List<string>{"1","2","4"};
we could get the items found in the list and not in the datatable this way:
var v = from r in ls
where !dt.Rows.Contains(r)
select r;
v.ToList();

With reasonable efficiency via HashSet<T> (and noting that the fastest way to get data out of a DataRow is via the DataColumn indexer):
HashSet<int> ids = new HashSet<int>();
DataColumn col = table.Columns["ID"];
foreach (DataRow row in table.Rows)
{
ids.Add((int)row[col]);
}
var missing = list.Where(item => !ids.Contains(item.ID)).ToList();

Related

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

How to create a DataTable with a single row and single key-value pair in that row.

For a test I'm trying to write, all I need is a DataTable with 1 row with ["foo"] = 2314 so that I reach the body of the loop
foreach(DataRow row in mockDataTable.Rows)
{
// ...
int val = (int)row["foo"];
// ...
}
inside a method that my test is testing.
However I can't figure out how to do this through the documentation on https://msdn.microsoft.com/en-us/library/system.data.datatable(v=vs.110).aspx
var dt = new DataTable();
dt.Columns.Add("foo", typeof (int));
var row = dt.NewRow();
row["foo"] = 1234;
dt.Rows.Add(row);

C# Group DataRows in DataTable

I have a DataTable with two columns: File & Email
C://file1.jpg aaa#gmail.com
C://file2.jpg aaa#gmail.com
C://file3.jpg bbb#gmail.com
C://file4.jpg ccc#gmail.com
C://file5.jpg bbb#gmail.com
In my code i loop through the DataRows and send an email to Email with File attached.
Problem:
I need to somehow check if there are any other DataRows with the same Email and if so, send just one email with multiple attachments.
So the above DataTable would result in 3 emails:
file1,file2 sent to aaa#gmail.com
file3,file5 sent to bbb#gmail.com
file4 sent to ccc#gmail.com
My code sample:
foreach (DataRow row in dt.Rows) {
string file = row[0].ToString();
string email = row[1].ToString();
SendEmailWithAttachments(email,file);
}
I could pass a StringCollection or an array to my SendEmailWithAttachments() function then loop throught it and attach all the files, but how do I group these DataRows in first place
Using the GroupBy Linq extension you could handle your DataRows grouping them for the Email field, then foreach create a list of strings with the file names.
Of course, you need also to change the SendMailWithAttachments to receive as second parameter a List<string> instead of a single string
var g = dt.AsEnumerable().GroupBy(d => d.Field<string>("Email"));
foreach (var x in g)
{
List<string> files = new List<string>();
foreach (var z in x)
files.Add(z.Field<string>("File"));
SendEmailWithAttachments(email,files);
}
You could use GroupBy to group by email:
DataTable dt = new DataTable();
dt.Columns.Add("Path");
dt.Columns.Add("Email");
DataRow dr = dt.NewRow();
dr.ItemArray=new object[2]{"C://file1.jpg", "aaa#gmail.com"};
dt.Rows.Add(dr);
dr = dt.NewRow();
dr.ItemArray=new object[2]{"C://file2.jpg", "aaa#gmail.com"};
dt.Rows.Add(dr);
dr = dt.NewRow();
dr.ItemArray=new object[2]{"C://file3.jpg", "bbb#gmail.com"};
dt.Rows.Add(dr);
dr = dt.NewRow();
dr.ItemArray=new object[2]{"C://file4.jpg", "ccc#gmail.com"};
dt.Rows.Add(dr);
dr = dt.NewRow();
dr.ItemArray=new object[2]{"C://file5.jpg", "bbb#gmail.com"};
dt.Rows.Add(dr);
var grouped=dt.AsEnumerable().GroupBy(x=>x.Field<string>("Email"));
foreach (var mail in grouped)
{
List<string> filesForEmail = new List<string>();
foreach (var file in mail)
{
filesForEmail.Add(file.Field<string>("Path"));
}
SendEmailWithAttachments(mail.Key, filesForEmail);
}

how to add some selected items and their indexes from listbox to datatable

I`m trying to use something like this:
I have 20 list items and I have to choose some of them, then add them to datatable in first column elements, in second - their indexes.
DataTable dt = new DataTable();
dt.Columns.Add("Фактор-Критерій", typeof(string));
dt.Columns.Add("Ранг", typeof(string));
ChoosenCriteria.DataSource = dt;
List<string> selectedItems = new List<string>();
foreach (string o in listBox1.SelectedItems)
selectedItems.Add(o);
List<int> selectedItemIndexes = new List<int>();
foreach (int o in listBox1.SelectedIndices)
selectedItemIndexes.Add(listBox1.Items.IndexOf(o));
DataRow dr = dt.NewRow();
dr[0] = selectedItems.ToString();
dr[1] = selectedItemIndexes.ToString();
dt.Rows.Add(dr););
I can not do this, all i have at the end is on screen:enter image description here
Move the DataSource binding statement after you have done with the modification like
for(int i=0; i<selectedItems.Count;i++
{
DataRow dr = dt.NewRow();
dr[0] = selectedItems[i].ToString();
dr[1] = selectedItemIndexes[i].ToString();
dt.Rows.Add(dr);
}
ChoosenCriteria.DataSource = dt;

Simple way to convert datarow array to datatable

I want to convert a DataRow array into DataTable ... What is the simplest way to do this?
For .Net Framework 3.5+
DataTable dt = new DataTable();
DataRow[] dr = dt.Select("Your string");
DataTable dt1 = dr.CopyToDataTable();
But if there is no rows in the array, it can cause the errors such as The source contains no DataRows. Therefore, if you decide to use this method CopyToDataTable(), you should check the array to know it has datarows or not.
if (dr.Length > 0)
DataTable dt1 = dr.CopyToDataTable();
Reference available at MSDN:
DataTableExtensions.CopyToDataTable Method (IEnumerable)
Why not iterate through your DataRow array and add (using DataRow.ImportRow, if necessary, to get a copy of the DataRow), something like:
foreach (DataRow row in rowArray) {
dataTable.ImportRow(row);
}
Make sure your dataTable has the same schema as the DataRows in your DataRow array.
DataTable dt = new DataTable();
DataRow[] dr = (DataTable)dsData.Tables[0].Select("Some Criteria");
dt.Rows.Add(dr);
Another way is to use a DataView
// Create a DataTable
DataTable table = new DataTable()
...
// Filter and Sort expressions
string expression = "[Birth Year] >= 1983";
string sortOrder = "[Birth Year] ASC";
// Create a DataView using the table as its source and the filter and sort expressions
DataView dv = new DataView(table, expression, sortOrder, DataViewRowState.CurrentRows);
// Convert the DataView to a DataTable
DataTable new_table = dv.ToTable("NewTableName");
Simple way is:
// dtData is DataTable that contain data
DataTable dt = dtData.Select("Condition=1").CopyToDataTable();
// or existing typed DataTable dt
dt.Merge(dtData.Select("Condition=1").CopyToDataTable());
DataTable Assetdaterow =
(
from s in dtResourceTable.AsEnumerable()
where s.Field<DateTime>("Date") == Convert.ToDateTime(AssetDate)
select s
).CopyToDataTable();
DataTable dt = myDataRowCollection.CopyToDataTable<DataRow>();
DataTable dt = new DataTable();
foreach (DataRow dr in drResults)
{
dt.ImportRow(dr);
}
.Net 3.5+ added DataTableExtensions, use DataTableExtensions.CopyToDataTable Method
For datarow array just use .CopyToDataTable() and it will return datatable.
For single datarow use
new DataRow[] { myDataRow }.CopyToDataTable()
You could use System.Linq like this:
if (dataRows != null && dataRows.Length > 0)
{
dataTable = dataRows.AsEnumerable().CopyToDataTable();
}
Here is the solution. It should work fine.
DataTable dt = new DataTable();
dt = dsData.Tables[0].Clone();
DataRows[] drResults = dsData.Tables[0].Select("ColName = 'criteria');
foreach(DataRow dr in drResults)
{
object[] row = dr.ItemArray;
dt.Rows.Add(row);
}
Incase anyone needs it in VB.NET:
Dim dataRow as DataRow
Dim yourNewDataTable as new datatable
For Each dataRow In yourArray
yourNewDataTable.ImportRow(dataRow)
Next
You need to clone the structure of Data table first then import rows using for loop
DataTable dataTable =dtExisting.Clone();
foreach (DataRow row in rowArray) {
dataTable.ImportRow(row);
}
DataTable dataTable = new DataTable();
dataTable = OldDataTable.Tables[0].Clone();
foreach(DataRow dr in RowData.Tables[0].Rows)
{
DataRow AddNewRow = dataTable.AddNewRow();
AddNewRow.ItemArray = dr.ItemArray;
dataTable.Rows.Add(AddNewRow);
}

Categories