I have been searching high and low for this to no avail. I have two DataTables that I want to join without creating a new resultant table as I simply need to update some rows in one of the tables to be displayed in a grid view, similar to the below code, but with a join:
sage_invoices.Select("CCE2 IS NULL")
.ToList<DataRow>()
.ForEach(row =>
{
row["Error"] = 1;
row["ErrorMessage"] = "Missing Region Code (Dimension 2 - CCE2)";
});
Everything I've found produces a new output datatable, similar to the below code:
var collection = from t1 in dt1.AsEnumerable()
join t2 in dt2.AsEnumerable()
on t1["id"] equals t2["id"]
select new { T1 = t1, T2 = t2 };
What I can't find is how to join two DataTables using .Join:
sage_invoices.Select()
.Join(<What Goes here?>)
.ToList<DataRow>()
.ForEach(row =>
{
row["Error"] = 1;
row["ErrorMessage"] = "ITMREF is not a Sage Product Code";
});
If anyone could point me in the right direction, I would be most grateful.
Thanks
Gareth
I typically accomplish this by building an anonymous object that contains a reference to my source and destination objects through a Join or GroupJoin, then looping over the result of the Join to update my destination object. See the example below.
Take a look at the documentation on Join and GroupJoin. Join is great for a 1-1 match, while GroupJoin is a 0-* match (like a SQL left join). The arguments to Join and GroupJoin allow you to specify a selector function for each IEnumerable followed by a selector function for the output object. Note that t1 and t2 below refer to table1 and table2.
using System;
using System.Data;
using System.Linq;
public class Program
{
public static void Main()
{
var table1 = GetEmptyTable();
table1.Rows.Add(1, "Old Value", false);
table1.Rows.Add(2, "Untouched Value", false);
var table2 = GetEmptyTable();
table2.Rows.Add(1, "New Value", false);
table2.Rows.Add(3, "Unused Value", false);
Console.WriteLine("Before...");
Console.WriteLine(PrintTable(table1));
var matched = table1.Select()
.Join(table2.Select(), t1 => (int)t1["A"], t2 => (int)t2["A"], (t1, t2)
=> new
{
DestinationRow = t1,
SourceRow = t2
});
foreach (var match in matched)
{
match.DestinationRow["B"] = match.SourceRow["B"];
match.DestinationRow["C"] = true;
}
Console.WriteLine("After...");
Console.WriteLine(PrintTable(table1));
}
private static DataTable GetEmptyTable()
{
var table = new DataTable();
table.Columns.Add("A", typeof(int));
table.Columns.Add("B", typeof(string));
table.Columns.Add("C", typeof(bool));
return table;
}
private static string PrintTable(DataTable table)
{
return string.Join(Environment.NewLine, table.Select().Select(x => "[" +
string.Join(", ", x.ItemArray) + "]"));
}
}
Related
This is propably answered somewhere else, but I haven't found working solution yet.
I have two datatables and I want to join them into one datatable containing all data from both of them, or at least from the first of them and some columns from the second datatable.
I don't want to list all columns (totally 180) from the first datatable. I have tried eg. this
var JoinedResult = from t1 in table1.Rows.Cast<DataRow>()
join t2 in table2.Rows.Cast<DataRow>()
on Convert.ToInt32(t1.Field<string>("ProductID")) equals t2.Field<int>("FuelId")
select t1;
but that gives only the columns from table1. How to get colums from table2 too to my result? Finally, I need to add my result to a dataset.
ResultSet.Tables.Add(JoinedResult.CopyToDataTable());
EDIT:
I ended up with this as the solution.
This follows an example given here Create join with Select All (select *) in linq to datasets
DataTable dtProduct = dsProduct.Tables[0];
DataTable dtMoistureLimits = ds.Tables[0];
//clone dt1, copies all the columns to newTable
DataTable dtProductWithMoistureLimits = dtProduct.Clone();
//copies all the columns from dt2 to newTable
foreach (DataColumn c in dtMoistureLimits.Columns)
dtProductWithMoistureLimits.Columns.Add(c.ColumnName, c.DataType);
var ProductsJoinedWithMoistureLimits = dtProduct.Rows.Cast<DataRow>()
.Join(dtMoistureLimits.Rows.Cast<DataRow>(),// join table1 and table2
t1 => new { ProductID = t1.Field<int>("ProductID"), DelivererID = t1.Field<int>("DelivererID") },
t2 => new { ProductID = t2.Field<int>("MoistureLimits_ProductID"), DelivererID = t2.Field<int>("MoistureLimits_DelivererID") },
(t1, t2) => // when they match
{ // make a new object
// containing the matching t1 and t2
DataRow row = dtProductWithMoistureLimits.NewRow();
row.ItemArray = t1.ItemArray.Concat(t2.ItemArray).ToArray();
dtProductWithMoistureLimits.Rows.Add(row);
return row;
});
However, in dtMoistureLimits there is not rows for all "ProductID" and "DelivererID" in dtProduct. Currently my solution returns only matching rows.
How to improve solution to return also those rows where there is not data for "ProductID" and "DelivererID" in dtMoistureLimits?
Solution using method syntax, without having to mention all columns
var result = table1.Rows.Cast<DataRow>()
.Join(table2.Rows.Cast<DataRow>(), // join table1 and table2
t1 => Convert.ToInt32(t1.Field<string>("ProductID")) // from every t1 get the productId
t2 => t2.Field<int>("FuelId") // from every t2 get the fuelId,
(t1, t2) => new // when they match
{ // make a new object
T1 = t1, // containing the matching t1 and t2
T2 = t2,
}
var JoinedResult = (from t1 in table1.Rows.Cast<DataRow>()
join t2 in table2.Rows.Cast<DataRow>()
on Convert.ToInt32(t1.Field<string>("ProductID")) equals t2.Field<int>("FuelId")
select new { T1 = t1,
T2 = t2.column_name // all columns needed can be listed here
}).ToList();
EDIT:
To convert the above result to a DataTable, use the following method:
DataTable dataTable = new DataTable();
//Get all the properties
PropertyInfo[] Props = JoinedResult.Select(y=>y.T1).First().GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (PropertyInfo prop in Props)
{
//Defining type of data column gives proper data table
var type = (prop.PropertyType.IsGenericType && prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) ? Nullable.GetUnderlyingType(prop.PropertyType) : prop.PropertyType);
//Setting column names as Property names
dataTable.Columns.Add(prop.Name, type);
}
dataTable.Columns.Add(t2_column_name, t2_column_type);
foreach (var item in JoinedResult)
{
var values = new object[Props.Length];
for (int i = 0; i < Props.Length; i++)
{
//inserting property values to datatable rows
values[i] = Props[i].GetValue(item.T1, null);
}
values[Props.Length] = item.T2;
dataTable.Rows.Add(values);
}
hi guy i have 2 datatable like this
dt1
id (1,2,3)
name (abc,xyz,def)
num(11,12,13)
dt2
id (1,2,3)
name (abc,xyz,def)
num_from (10,13,11)
num_to (14,14,14)
how could i select id which have num between num_from and num_to using linq
i tried this
dtres = (from t1 in dt1.AsEnumerable()
join t2 in dt1.AsEnumerable() on t1.Field<string>("ID") equals t2.Field<string>("ID")
where t1["num"]>= t2["num_from"] &&
t1["num"]<= t2["num_to"]
select t1).CopyToDataTable();
Consider the following code:
It produce the result as IEnumerable<AnonymousType>, not DataRow, so cannot apply CopyToDataTable() extension method, instead I have provided a custom extension method at the bottom of this code ToDataTable, you can change the number of columns from the final result, I have included everything.
My understanding from your question is you need a filter such that Num in DataTable1 is between Num_From and Num_To in the Datatable2
var resultDataTable =
dt1.AsEnumerable().Join(dt2.AsEnumerable(), t1 => t1["id"], t2 => t2["id"], (t1, t2) => new { t1, t2})
.Where(t => (int.Parse(t.t2["num_from"].ToString()) <= int.Parse(t.t1["num"].ToString()) && int.Parse(t.t2["num_to"].ToString()) >= int.Parse(t.t1["num"].ToString())))
.Select(t => new {
Id1 = t.t1["id"].ToString(),
Name1 = t.t1["name"].ToString(),
Num1 = t.t1["num"].ToString(),
Id2 = t.t2["id"].ToString(),
Name2 = t.t2["name"].ToString(),
Num_From = t.t2["num_from"].ToString(),
Num_To = t.t2["num_to"].ToString()
}
).ToList().ToDataTable();
Extension method to convert IEnumerable to DataTable
public static class ExtensionDT
{
public static DataTable ToDataTable<T>(this List<T> items)
{
var tb = new DataTable(typeof(T).Name);
PropertyInfo[] props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var prop in props)
{
tb.Columns.Add(prop.Name, prop.PropertyType);
}
foreach (var item in items)
{
var values = new object[props.Length];
for (var i = 0; i < props.Length; i++)
{
values[i] = props[i].GetValue(item, null);
}
tb.Rows.Add(values);
}
return tb;
}
}
Creating a join operation on Linq database is not possible like we do on Mysql and sql. But you can create a simple function to help you do that. You will need a function to return a string or interger for you:
private ObservableCollection<Var_Items> _var_ItemsList;
public ObservableCollection<Var_Items> Var_ItemsList
{ get { return _var_ItemsList; }
set { _var_ItemsList = value;
NotifyPropertyChanged("Var_ItemsList");
}
}
dtres = from t1 in dt1.AsEnumerable() where t1["num"]>= getMyVar1(t1["num"]) and t1["num"]<= getMyVar1(t1["num"]) select t1;
public string getMyVar1(int find_var)
{
var thisvar = from t2 in dt2.AsEnumerable() where t2["num_from"] >= find_var select t2;
varitems = new ObservableCollection<Var_Items>(Var_ItemsList);
return varitems.Last();
}
public string getMyVar2(int find_var)
{
var thisvar = from t2 in dt2.AsEnumerable() where t2["num_to"] >= find_var select t2;
varitems = new ObservableCollection<Var_Items>(Var_ItemsList);
return varitems.Last();
}
I have tried to simplify my answer to be easier to understand. I hope this helps
Trying to do what a SQL query (SELECT DISTINCT (first,second),third FROM table) would do but I am doing it with LINQ and a datatable.
EDIT
SQL should look like a Mysql
select first, second, third
FROM table
group by first, second
DataTable secondTable = new DataTable();
secondTable.Columns.Add("name", typeof(string));
secondTable.Columns.Add("date", typeof(string));
secondTable.Columns.Add("clockIn", typeof(string));
secondTable.Columns.Add("clockOut", typeof(string));
var t4 = (from a in firstTable.AsEnumerable()
select new
{
name = a.Field<string>("name"),
date = a.Field<string>("date"),
clockIn = a.Field<string>("clockIn"),
clockOut = a.Field<string>("clockOut")
}).Distinct();
var t5 = (from a in firstTable.AsEnumerable()
select new
{
name = a.Field<string>("name"),
date = a.Field<string>("date")
}).Distinct();
var t6 = (from d in t5
join a in t4
on new
{
d.name,
d.date
}
equals new
{
a.name,
a.date
}
select secondTable.LoadDataRow(
new object[]
{
d.name,d.date,a.clockIn,a.clockOut
}, false)).ToList();
ViewBag.Data = secondTable;
What this code is doing is, it is joining t4 and t5 in t6 with no exclusions. While what I desire is all rows from t4 that are present in t5 should join with t6 on the basis on (name, date) AND all rows from t5that don't exist in t4 should be excluded. Can anyone please help?
From your comments, you may just group by the desired fields, and take any of the grouped result.
You may order by clockin or clockout to get a less "random" result.
var t6 = firstTable.AsEnumerable()
.GroupBy(a => new {
name = a.Field<string>("name"),
date = a.Field<string>("date")
}
)
.Select(g => g.First())
//or Select(g => g.OrderBy(a => a.Field<string>("clockIn")).First()
.ToList();
I have two DataTables t1 and t2. I'm trying to perform a LINQ left join, multiple equijoin, to get the DataRows in t1 that are not in t2.
In SQL, what I'm trying to accomplish is:
select t1.*
from t1
left join t2
on t1.a=t2.a and
t1.b=t2.b and
t1.c=t2.c
where
t2.a is null
So far I have the following:
public DataTable t1_without_t2(DataTable t1, DataTable t2)
{
var query = from t1_row in t1.AsEnumerable()
join t2_row in t2.AsEnumerable()
on
new { t_a = t1_row["a"], t_b = t1_row["b"], t_c = t1_row["c"]}
equals
new { t_a = t2_row["a"], t_b = t2_row["b"], t_c = t2_row["c"]}
into leftJoinT1withoutT2
from join_row in leftJoinT1withoutT2.DefaultIfEmpty()
where t2_row["a"] == null
select new
{
j_a = join_row["a"],
j_b = join_row["b"],
j_c = join_row["c"],
};
DataTable dt = t1.Clone();
foreach (var result in query)
{
dt.LoadDataRow(
new object[]
{
result.j_a,
result.j_b,
result.j_c
},
false);
}
return dt;
}
This is failing on the line j_a = join_row["a"] with this message:
Column 'a' does not belong to table.
I thought that the into leftJoinT1withoutT2 line was supposed to put the results of the join into a var with the column structure of table t1, from which the non-matching entries would be removed using where t2_row["a"] == null . Is that not what's happening here? I'm a little confused.
It should look like this:
var query = from t1_row in t1.AsEnumerable()
join t2_row in t2.AsEnumerable()
on
new { t_a = t1_row["a"], t_b = t1_row["b"], t_c = t1_row["c"] }
equals
new { t_a = t2_row["a"], t_b = t2_row["b"], t_c = t2_row["c"] }
into leftJoinT1withoutT2
from join_row in leftJoinT1withoutT2.DefaultIfEmpty()
.Where(r => r == null)
select new
{
j_a = t1_row["a"],
j_b = t1_row["b"],
j_c = t1_row["c"],
};
Have a look at How to: Perform Left Outer Joins (C# Programming Guide).
The join_row gets null (i.e. default TSource value, see Enumerable.DefaultIfEmpty) when there is no matching element in t2, while t1_row always contains the joined value. So as far as you need only those rows for which join_row is null, I used .Where(r => r == null).
Can someone help me to translate this
var query = from s in context.ShoppingMalls
join h in context.Houses
on
new { s.CouncilCode, s.PostCode }
equals
new { h.CouncilCode, h.PostCode }
select s;
into lambda query?
Thanks.
var query = context.ShoppingMalls
.Join(
context.Houses,
s => new { s.CouncilCode, s.PostCode },
h => new { h.CouncilCode, h.PostCode },
(s, h) => s);
Although the example and answer given by #Thomas Levesque works for columns that match, I wanted to also supply the answer if you have columns to join on but they have different names. This is what I needed for my googling and this question got me close.
The difference of course is the explicit declaration of the columns as a variable to identify on.
var query = context.MapKitsToResources
.Join(
context.Resources,
o => new { Id = o.ResourceId, Type = o.ResourceTypeId},
i => new { Id = i.Id, Type = TypeId},
(o, i) = new { rType : i };