List<T> joins DataTable - c#

I have a List of objects (lst) and DataTable (dt). I want to join the lst and dt on the common field (code as string) and need to return all matching rows in the lst.
My List contains two columns i.e code and name along with values below:
code name
==== ====
1 x
2 y
3 z
The DataTable contains two columns i.e code and value along with values below:
code value
==== =====
3 a
4 b
5 c
The result is:
3 z
Below is my code; but I know it is not a correct statement and thus seeking your advice here. I would be much appreciated if you could guide me on how to write the correct statement.
var ld = from l in lst
join d in dt.AsEnumerable() on l.code equals d.code
select new { l.code, l.name };

You can use Linq query or Join extension method to join the collection on code. Just that when you select data from datatable, you need to use dt.Field method. Please use either of the following code.
Query1:
var ld = lst.Join(dt.AsEnumerable(),
l => l.code,
d => d.Field<string>("code"),
(l, d) => new
{
l.code,
l.name,
value = d.Field<string>("value")
}).ToList();
Query2:
var ld = (from l in lst
join d in dt.AsEnumerable()
on l.code equals d.Field<string>("code")
select new
{
l.code,
l.name,
value = d.Field<string>("value")
}).ToList();
Query3:
var ld = (from l in lst
join d in dt.AsEnumerable()
on l.code equals d.Field<string>("code")
let value = d.Field<string>("value")
select new
{
l.code,
l.name,
value
}).ToList();

You can try any of the below.
var ld = from l in lst
join d in dt.AsEnumerable() on l.code equals d.Field<int>("code")
select new { l.code, l.name };
var ld = lst.Join(dt.AsEnumerable(), l => l.code, d => d.Field<int>("code"), (l,d) => new { l.code, l.name });

It's not clear what your required output is but it looks as if you are correctly getting the only common records. You could extend your select to
select new { l.code, l.name, d.value }
Which would give all the data/columns from both tables.
code name value
==== ==== =====
3 z a

Try this:
var ld = from l in lst
join d in dt.Cast <DataRow>() on l.code equals d["code"].ToString()
select new { l.code, l.name };

SO you have a List and a DataTable. You don't plan to use the Values of the DataTable, only the Codes.
You want to keep those List items, that have a Code that is also a code in the DataTable.
If you plan to use your DataTable for other things than just for this problem, My advice would be to first create a procedure to convert your DataTable into an enumerable sequence.
This way you can add LINQ statements, not only for this problem, but also for other problems.
Let's create an extension method for your DataTable that converts the data into the items that are in the DataTable. See extension methods demystified.
Alas, I don't know what's in your DataTable, let's assume that your DataTable contains Orders
class CustomerOrder
{
public int Id {get; set;}
public int CustomerId {get; set;}
public int Code {get; set;}
public string Value {get; set;}
...
}
The extension method that extends functionality of class DataTable:
public static IEnumerable<Order> ToCustomerOrders(this DataTable table)
{
return table.AsEnumerable().Select(row => new CustomerOrder
{
Id = ...
CustomerId = ...
Code = ...
Value = ...
};
}
I'm not really familiar with DataTables, but you know how to convert the cells of the row into the proper value.
Usage:
DataTable table = ...
Int customerId = 14;
var ordersOfThisCustomer = table.ToCustomerOrders
.Where(customerOrder => customerOrder.CustomerId == customerId)
.FirstOrDefault();
In words: convert the datatable into CustomerOrders, row by row, and check for every converted CustomerOrder whether it has a CustomerId equal to 14. Stop if found. return null if there is no such row.
Now that you've got a nice reusable procedure that is also easy to test, debug and change, we can answer your question.
Given a DataTable with CustomerOrders, and a sequence of items that contain Code and Name, keep only those items from the sequence that have a Code that is also a Code in the DataTable.
var dataTable = ... // your DataTable, filled with CustomerOrders.
var codeNames = ... // your list with Codes and Names
var codesInDataTable = dataTable.ToCustomerOrders
.Select(customerOrder => customerOrder.Code)
.Distinct();
This will create an enumerable sequence that will convert your DataTable row by row and extract property Code. Duplicate Code values will be removed.
If Codes are unique, you don't need Distinct.
Note: the enumerable sequence is not enumerated yet!
var result = codeNames
.Where(codeName => codesInDataTable.Contains(codeName.Code))
.ToList();
In words: for every [Code, Name] combination in your list, keep only those [Code, Name] combinations that have a value for Code that is also in codesInDataTable.

Related

C# Lambda Join Retrieve First Row Values

I have two tables, Table1 and Table2. Table1 has 1 to many relationship with Table2.
Table1 has ListId, ListName
Table2 has FirstName, LastName, Phone, ListId, CustomField1, CustomField2
What I want to retrieve is All rows from Table1 but only retrieve CustomField2 value of Table2 for only 1st row.
The existing query I have below is retrieving me all rows from Table2 which I do not want.
var result = _db.Lists
.Join(_db.ListUsers, c => c.ListID, d => d.ListID, (c, d) => new { c, d });
My final resultset need to look like this
Table1.ListId, Table1.ListName, Table2.CustomField2
1, first list, "abc"
2, second list, "def"
This should do the trick, get only the first record on the right table:
from i in _db.Lists
let p = _db.ListUsers.Where(p2 => i.ListID == p2.ListID).FirstOrDefault()
select new
{
ListID = i.ListID,
ListName = i.ListName,
CustomField2 = p.CustomField2
}
With lambda expression, it would be:
_db.Lists
.Select (
i =>
new
{
i = i,
p = _db.ListUsers
.Where (p2 => i.ListID == p2.ListID))
.Take(1)
.FirstOrDefault()
})
.Select (
results =>
new
{
ListID= results.i.ListID,
ListName = results.i.ListName,
CustomField2 = results.p.CustomField2
}
)
From what I read, the result you want to achieve is retrieve a list of results contains All columns from Table1 & 1 column from Table2 (Not rows).
There are few ways to do this. I would use create a DTO class to retrieve my results. And in the select , you would need to specifically list out the item in your select result.
For example,
Create a DTO class
public class DTOListResult
{
public string XXX {get; set;}
...
}
Then in your result, you can write in this way.
var result = (from a in _db.Lists select new DTOListResult { XXX = _db.table2.ID,
xxx = a.ID,
XXX = a.XX});

How to assign values to a list by linq without for or foreach statements?

I want to compare two lists and assign 1st list to another in case of requirement.
var getdetail=_readonlyservice.getdetail().ToList();
foreach(var item in docdetail)
{
var temp=getdetail.firstordefualt(i=>i.Id=item.Id)
if(temp==null) continue;
item.code=temp.code;
}
I want to implement top statements in linq .any help ?
Think so..
var getdetail=_readonlyservice.getdetail().ToList();
var tempList = from dd in context.docdetail
join g in context.getdetail on dd.Id equals g.Id
select new // Your type
{
// Columns...
Code = g.Code
}
I believe you are trying to do like the way I did, although I was going to join table.
var result = (from e in DSE.employees
join d in DSE.departments on e.department_id equals d.department_id
join ws in DSE.workingshifts on e.shift_id equals ws.shift_id
select new
{
FirstName = e.FirstName,
LastName = e.LastName,
Gender = e.Gender,
Salary = e.Salary,
Department_id = e.department_id,
Department_Name = d.department_name,
Shift_id = ws.shift_id,
Duration = ws.duration,
}).ToList();
// TODO utilize the above result
I was using DTO method to do this. And then you return result(as this case is result).
You may view the whole question and solution here.
As this case, you are not required to put foreach loop, as the query said from every row in yourdatabase.table

Convert SQL into linq to sql - group by two columns and request count of a third column

This is how the query looks in SQL:
select count(SnpId_this), DrugId, VariantId from tbl_custom_SNPs_All
join tbl_custom_SNP_Variants on VariantId = SnpsVariantId_this
join tbl_custom_SNP_Drugs_Apelon_NUIs on DrugId = SnpsDrugId_this
group by DrugId, VariantId
Here is my attempt using linq-to-sql:
var drugVariantGroups =
(from a in adminDB.tbl_custom_SNPs_Alls
join v in adminDB.tbl_custom_SNP_Variants
on a.VariantId equals v.SnpsVariantId_this
join d in adminDB.tbl_custom_SNP_Drugs_Apelon_NUIs
on a.DrugId equals d.SnpsDrugId_this
group a by new { a.VariantId, a.DrugId } into dv
select new
{
dv.Key.VariantId,
dv.Key.DrugId,
Entries = dv.Sum()
}).ToList();
looks like dv does not have a definition for sum. How do I access SnpId_this to count it?
Just use Count. You may need to filter any entries where SnpId_this is null if that is a possibility and to precisely match the T-SQL.
Entries = dv.Where(t => t.SnpId_this != null).Count()
You want Count not Sum:
var drugVariantGroups =
(from a in adminDB.tbl_custom_SNPs_Alls
join v in adminDB.tbl_custom_SNP_Variants
on a.VariantId equals v.SnpsVariantId_this
join d in adminDB.tbl_custom_SNP_Drugs_Apelon_NUIs
on a.DrugId equals d.SnpsDrugId_this
group a by new { a.VariantId, a.DrugId } into dv
select new
{
dv.Key.VariantId,
dv.Key.DrugId,
Entries = dv.Count()
}).ToList();
If you want sum you should provide the field to use in Sum function like:
dv.Sum(c=>c.Amount)

How to convert to int and then compare in linq query c#

IEnumerable<classB> list = getItems();
//dt is datatable
list = list.Where(x => Convert.ToInt32( !dt.Columns["Id"]) == (x.Id));
I want to only keep the items in the list which match in datatable id column. The rest are removed. I m not doing it right.
The datatable can have: ID - 1,3,4,5,7
The list can have: ID - 1,2,3,4,5,6,7,8,9,10
I want the output list to have: ID - 1,3,4,5,7
Your code won't work because you're comparing a definition of a column to an integer value. That's not a sensible comparison to make.
What you can do is put all of the values from the data table into a collection that can be effectively searched and then get all of the items in the list that are also in that collection:
var ids = new HashSet<int>(dt.AsEnumerable()
.Select(row => row.Field<int>("Id"));
list = list.Where(x => ids.Contains(x.Id));
Try this one
var idList = dt.AsEnumerable().Select(d => (int) d["Id"]).ToList();
list = list.Where(x => idList.Contains(x.Id));
You can't do it like that. Your dt.Columns["Id"] returns the DataColumn and not the value inside that column in a specific datarow. You need to make a join between two linq query, the first one you already have, the other you need to get from the DataTable.
var queryDt = (from dtRow in dt
where !dtRow.IsNull("Id")
select int.Parse(dtRow["Id"])).ToList();
Now the join
var qry = from nonNull in queryDt
join existing in list on nonNull equals list.id

Linq Query Grouping Data table using two columns

I've got one question here. I'm a newbie so pardon with my terminologies, I am querying a data table wherein I need to group this data table according to date and their unique access code.
var tMainTable = (from System.Data.DataRow b in _tData.data_table.Rows
group b by b["ACCESS_CODE"] into bGroup
select new
{ bGroup });
in my current grouping above, I am grouping my data table according to access code. My data table is composed of 3 fields: DATE, ACCESS_CODE, COUNT. This is provided that I cant make my datatable AsEnumerable() type.
So this time, I want to add in its condition, which is grouping by date as well... is there such thing as:
var tMainTable = (from System.Data.DataRow b in _tData.data_table.Rows
**group b by b["ACCESS_CODE"] AND b["DATE"] into bGroup**
select new
{ bGroup });
Thanks for any inputs.
Use an anonymous type for your grouping:
var codeDateGroups = _tData.data_table.AsEnumerable()
.GroupBy(r => new {
AccessCode = r.Field<string>("ACCESS_CODE"),
Date = r.Field<DateTime>("DATE")
});
You can access it via the Key:
foreach(var group in codeDateGroups)
Console.WriteLine("Code=[{0}] Date=[{1}]"
, group.Key.AccessCode
, group.Key.Date);
var tMainTable = (from System.Data.DataRow b in _tData.data_table.Rows
group b by new { AccessCode = b["ACCESS_CODE"], Date = b["DATE"] } into bGroup
select new
{ bGroup });
var groups = _tData.data_table.AsEnumerable()
.GroupBy(row=> new {row["ACCESS_CODE"],row["DATE"] });

Categories

Resources