Help with querying a DataTable using LINQ - c#

Suppose I have this initial code:
DataTable table = new DataTable();
table.Columns.Add("column1", typeof(int));
table.Columns.Add("column2", typeof(int));
table.Columns.Add("column3", typeof(string));
table.Rows.Add(1, 0, "a");
table.Rows.Add(2, 1, "b");
table.Rows.Add(3, 1, "c");
table.Rows.Add(4, 3, "d");
table.Rows.Add(5, 3, "e");
How can I do these using LINQ?
a. Return the DataRows whose values in column1 also appears in column2.
So far, I did this:
var x = (from t1 in table.AsEnumerable()
select t1.Field<int>(0)).Intersect
((from t2 in table.AsEnumerable()
select t2.Field<int>(1)).Distinct());
Bu the problem is, only the values of column1 is returned, which I use a foreach on. Probably because of the select t1.Field<int>(0) part, but I don't know how to return the DataRows itself.
b. Return the values of column3 whose values in column1 also appears in column2.
Almost the same question as [a]. I can only return the column1 row since I already used it. I don't know how to return the DataRows and other columns (e.g. column3) except column1.
I have also tried this:
var x1 = from t in table.AsEnumerable()
select t;
var x2 = (from t in table.AsEnumerable()
select t.Field<int>(1)).Distinct();
I was hoping to use Intersect() on x1 and x2, but I don't know how. Especially since x1 is kind of like a DataRow[] and x2 like an int[].
c. Using the answer in [a] for another query.
Or using something from a LINQ for another LINQ. i have no idea at all how to do something like this.

An approach:
a) var result = (from t1 in table.AsEnumerable()
join t2 in table.AsEnumerable() on t1.Field<int>(0) equals t2.Field<int>(1) select t1).Distinct();
The query above returnsIEnumerable<DataRow>.From this result you can select values of column3 like t2.Field<int>(2) for b) scenario.

I would create a new class for the three columns. Then create an Iqueryable or List for the new class and add the table rows into that. Then the Linq expression should work.
Class
public class myClass
{
public int column1
{
get;
set;
}
public int column2
{
get;
set;
}
public stringcolumn3
{
get;
set;
}
}
Linq
a. Return the DataRows whose values in column1 also appears in column2.
var x = (from l1 in myList
where (from l2 in myList
select l2.column2).contains(l1.column1)
select l1);
b. Return the values of column3 whose values in column1 also appears in column2.
var col3Values = (from l1 in myList
where l1.column2 = l1.column3
select l1.column3);

With help from guys here, and some other sites, I just found out how to actually do [b] above.
This returns the values in column3 whose values in column1 does not appear in column2:
from t in table.AsEnumerable()
join t2 in table.AsEnumerable().Select(i => i.Field<int>(0)).Except(table.AsEnumerable().Select(j => j.Field<int>(1)))
//the inner Select() returns column1 whose values in it also appears in column2
//I can use either this or the first LINQ I made above
//By the way, I said **does not** because I don't think I can use inner join on the opposite of [b]
//unlike the Select() with lambda above; I can just change the Intersect() to Except() :)
on t.Field<int>(0) equals t2
where t.Field<int>(1) > 2 //If I need some other condition
select t.Field<string>(2);
For [c], I made another table:
DataTable tableA = new DataTable();
tableA.Columns.Add("columnA", typeof(string));
tableA.Columns.Add("columnB", typeof(string));
tableA.Rows.Add("apple", "red");
tableA.Rows.Add("banana", "yellow");
tableA.Rows.Add("carrot", "orange");
tableA.Rows.Add("dog", "commonly brown"); //ok, I can't think of a fruit/vegetable that starts with 'd' right now...
tableA.Rows.Add("eggplant", "purple");
And renamed the first table to table1 to avoid/minimize confusion
var x = from tA in tableA.AsEnumerable()
from t1 in (
from t1 in table1.AsEnumerable()
join t2 in ((from t2_1 in table1.AsEnumerable()
select t2_1.Field<int>(0)).Except
((from t2_2 in table1.AsEnumerable()
select t2_2.Field<int>(1))).Distinct())
on t1.Field<int>(0) equals t2
where t1.Field<int>(1) > 2 //extra condition
select t1.Field<string>(2))
where tA.Field<string>(0).StartsWith(t1)
select tA;
This returns Rows in tableA whose columnA starts with the returned table1's column3, whose column1 values does not appear in column2 and has a value greater than 2 in its column2.

Related

How to select all columns in LINQ Datatable join?

var collection = from t1 in dt1.AsEnumerable()
join t2 in dt2.AsEnumerable()
on t1["id"] equals t2["id"]
select new { Name = t1["name"], Group = t2["group"] };
I want to select all columns of both table like join in SQL Server inner join query.
In Addition
How can i convert whole result of both tables to data-table?
var collection = from t1 in dt1.AsEnumerable()
join t2 in dt2.AsEnumerable()
on t1["id"] equals t2["id"]
select new { T1 = t1, T2 = t2 };
then...
EDIT:
Something along those lines
//clone dt1, copies all the columns to newTable
DataTable newTable = dt1.Clone();
//copies all the columns from dt2 to newTable
foreach(var c in dt2.Columns)
newTable.Columns.Add(c);
//now newTable has all the columns from the original tables combined
//iterates over collection
foreach (var item in collection) {
//creates newRow from newTable
DataRow newRow = newTable.NewRow();
//iterate the columns, gets values from either original table if column name is there
foreach(var c in newTable.Columns)
newRow[c.ColumnName] = item.T1.ContainsColumn(c.ColumnName) ? item.T1[c.ColumnName] : item.T2[c.ColumnName];
newTable.Rows.Add(newRow);
}
This will work. But if dt1 and dt2 share multiple columns with the exact same name, you might have some loss of data.
While you can't expand them to columns, you can simply return the entities. Eg:
select new { CTLJCRJOB, CTLRFDSTM }
If you need it flattened, then you will have to write out the mapping yourself, but will still be very trivial.
Referenced from:
Select All columns for all tables in join + linq join
ou have to specify each manually if you want to project into a flattened type. Your other option is to just have your combined type contain both objects, and the objects will naturally bring along their properties.
select new
{
Object1 = object1,
Object2 = output
};
And you would work with it like myObj.Object1.Property1, myObj.Object2.Property4, etc.
One final option that still involves some manual work is to define an appropriate type and have a constructor or a builder method that does the work of segmenting out your object properties into a flattened type. You still perform the manual mapping, but you isolate it from your query logic.
select new CombinedType(object1, output);
//or
select builder.GetCombinedType(object1, output);
Referenced From
Select all columns after JOIN in LINQ
var collection = (from t1 in dt1.AsEnumerable()
join t2 in dt2.AsEnumerable()
on t1 ["id"] equals t2 ["id"]
select new { Name = t1 ["name"], Group = t2 ["group"] }).ToList() ;
Hope this will help

i have to compare two datatables c# asp.net, compare its two columns and add that resultant column in the new datatable

I have tried this , but i don't know how to manipulate LINQ query to find difference between two columns from the data table.
I have two datatables dtStockTransactionData and dtStocksData. Below code explains how I have manupulated the result
DataTable dtMerged = (from a in dtStockTransactionData.AsEnumerable()
join b in dtStocksData.AsEnumerable()
on a["exchangesymbol"].ToString() equals b["exchange_symbol"].ToString()
into g
where g.Count() > 0
select a).CopyToDataTable();
grdErrSTSData.DataSource = dtMerged;
grdErrSTSData.DataBind();
return dtMerged;
If I understood your question you can do this using link query with select new
keyword.This is a example for Link with join -
from a in db.table1
join b in db.table2 on a.valueA equals b.valueB
select new { A = a.valueA, B = valueB};
As your code:
var selected = from a in dtStockTransactionData.AsEnumerable()
join b in dtStocksData.AsEnumerable()
on a["exchangesymbol"].ToString() equals b["exchange_symbol"].ToString()
into g
where g.Count() > 0
select new{
value1 = a.wantedValue,
value2 = b.wantedValue
}
Then you can get result using selected
After that you can compare data in own way.
Refer this Return list using select new in LINQ

getting non matched values from two Datatable

Is there any direct method for getting non matched values from two data table. I have one datatable which returns all the groups from Active Directory, and another datatable consist of all the group names from sharepoint list. But i need the non matched values by comparing these two datatables. please help me, if it possible.
Thanks in advance.
You could use DataRowComparer to compare the rows.
For instance, to compare the first rows of 2 data tables:
DataRow left = table1.Rows[0];
DataRow right = table2.Rows[0];
IEqualityComparer<DataRow> comparer = DataRowComparer.Default;
bool bEqual = comparer.Equals(left, right);
You can use .Except to do this. (Assuming an ID column)
IEnumerable<int> idsInDataTableA = dataTableA.AsEnumerable().Select(row => (int)row["ID"]);
IEnumerable<int> idsInDataTableB = dataTableB.AsEnumerable().Select(row => (int)row["ID"]);
IEnumerable<int> difference = idsInDataTableA.Except(idsInDataTableB );
I want compare DataTable1 that not exist in DataTable2
You can use Linq. Very efficient approaches are Enumerable.Except or Enumerable.Join(as LEFT OUTER JOIN) which are using sets:
var keyColRows = dt1.AsEnumerable()
.Select(r => r.Field<int>("KeyColumn")
.Except(dt2.AsEnumerable().Select(r2 => r2.Field<int>("KeyColumn"));
foreach(int inTable2Missing)
Console.WriteLine(inTable2Missing);
or the Join approach selecting the whole DataRow:
var rowsOnlyInDT1 = from r1 in dt1.AsEnumerable()
join r2 in dt2.AsEnumerable()
on r1.Field<int>("KeyColumn") equals r2.Field<int>("KeyColumn") into groupJoin
from subRow in groupJoin.DefaultIfEmpty()
where subRow == null
select r1;
Here you can use rowsOnlyInDT1.CopyToDataTable to create a new DataTable of the rows in table1 which are unique/new or use foreach to enumerate them.

LINQ to SQL: How to implement a LEAST or similar function?

Given:
A table named TABLE_1 with the following columns:
ID
ColumnA
ColumnB
ColumnC
ColumnD
I have SQL query where TABLE_1 joins on itself twice based off of ColumnA, ColumnB, ColumnC. The query might look something like this:
Select t1.ID, t2.ID, t3.ID, LEAST(t1.ColumnD, t2.ColumnD, t3.ColumnD)
From TABLE_1 t1
Left Join TABLE_1 t2 On
t1.ColumnA = t2.ColumnA
And t1.ColumnB = t2.ColumnB
And t1.ColumnC = t2.ColumnC
Left Join TABLE_1 t3 On
t2.ColumnA = t3.ColumnA
And t2.ColumnB = t3.ColumnB
And t2.ColumnC = t3.ColumnC
Problem:
I need that Query to be rewritten in LINQ. I've tried taking a stab at it:
var query =
from t1 in myTABLE1List // List<TABLE_1>
join t2 in myTABLE1List
on new {t1.ColumnA, t1.ColumnB, t2.ColumnC}
equals new {t2.ColumnA, t2.ColumnB, t2.ColumnC}
join t3 in myTABLE1List
on new {t2.ColumnA, t2.ColumnB, t2.ColumnC}
equals new {t3.ColumnA, t3.ColumnB, t3.ColumnC}
select new {
ID_1 = s1.ID,
ID_2 = s2.ID,
ID_3 = s3.ID,
// Invalid anonymous type member declarator.
// Anonymous type members must be declared with a member assignment,
// simple name or member access.
// how can I implement this?
least(s1.ColumnD, s2.ColumnD, s3.ColumnD)
};
....
private object least(params object[] objects)
{
// code here that sorts the objects and returns the 'smallest' of them.
return leastObject;
}
How do I write my query in LINQ? What am I doing wrong? I thought it was possible to use functions inside of LINQ expressions, so why am I getting this error?
I could be wrong, but I think you need to assign the result of least to an actual member of your new anonymous type instance, e.g.:
select new {
ID_1 = s1.ID,
ID_2 = s2.ID,
ID_3 = s3.ID,
Least = least(s1.ColumnD, s2.ColumnD, s3.ColumnD)
};
How about?
MinOfColumnD = Math.Min(Math.Min(s1.ColumnD, s2.ColumnD), s3.ColumnD))
EDIT: Assuming that ColumnD in each of the table is numeric & 'least` (in this context) is minimum of 3 numbers in the given columns.
EDIT: If the columns are string, determine what max. values it can hold & do a .Parse on it.
For e.g.
MinOfColumnD = Math.Min(Math.Min(int.Parse(s1.ColumnD), int.Parse(s2.ColumnD)), int.Parse(s3.ColumnD)))

Linq return all columns from all tables in the join

Lets say I have 2 tables both of them contain dynamic columns and I wish to retrieve a collection of datarow with all the columns from both the tables(later i will bind it to a grid view) after performing left outer join.
Sample Query:
var query = from TableA in ds.Tables[0].AsEnumerable()
join TableB in ds.Tables[1].AsEnumerable() on new { col1 = TableA.Field<Int32>("colA"), col2 = TableA.Field<DateTime>("colB") }
equals new { col1 = TableB.Field<Int32>("colA"), col2 = TableB.Field<DateTime>("colB") }
into GJ
from sub in GJ.DefaultIfEmpty()
select TableA;
Problem:
I want to select tableA and tableB together. The above sample query works and it populates all columns of tableA after left outer join. But i wish to retrieve all the columns from both the tables. Please advice.
Just select both parts into an anonymous type:
var query = from TableA in ds.Tables[0].AsEnumerable()
join TableB in [ ...] on [...] equals [...] into GJ
from sub in GJ.DefaultIfEmpty()
select new { RowA = TableA, RowB = sub };
Each element of the result will have two properties: RowA being a row from TableA, and RowB being a matching row from TableB or null if no rows from TableB matches RowA.

Categories