I have a List and a DataTable which contains a column to match the IDs in the list. I need to identify all IDs in my list that are not in the DataTable. I tried getting an IEnumberable DataRow and joining that to the list but I'm not able to identify the missing ones.
Here is my code and what I've tried...
List<int> JobIdList = (from i in EntryItems select i.JobID.Value).ToList<int>();
IEnumerable<DataRow> rowInfo = JobBLL.JobsExist(JobIdList).AsEnumerable();
var MissingList = (from rec in rowInfo
join id in JobIdList
on rec.Field<int>("JobID") equals id
into grouping
from id in grouping.DefaultIfEmpty()
select new { id }).ToList();
if (MissingList.Count > 0) { // Show message and exit }
The problem is that this returns the items in the data table that ARE found. Let's say I have 1, 2, and 3 in my list but my data table only has 1 and 3. I want to have 2 in MissingList.
Any thoughts?
var jobIdList = new HashSet<int>(from i in EntryItems select i.JobID.Value);
var rows = JobBLL.JobsExist(jobIdList).AsEnumerable();
var foundIds = (from x in rows select x.Field<int>("JobID")).ToList();
var missingIds = jobIdList.Except(foundIds);
You need to add the below line of code into your code.
var missingIds = JobIdList.Except(MissingList);
Related
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.
//SELECT table1.GG_ITEM, Sum(table1.REM_QTY) AS SumPerGG_ITEM
//FROM table1
//WHERE (table1.SUGG_DOCK_DATE Is Not Null)
//GROUP BY table1.GG_ITEM
//ORDER BY table1.GG_ITEM;
var try1 = (from row in db2.Dumps select new { Type1 = row.GA_ITEM, Type2 = row.REM_QTY });
Debug.Print(":::::try1:::::");
foreach (var row in try1)
{
Debug.Print(row.Type1.ToString());
Debug.Print(row.Type2.ToString());
}
var try2 = (from row in db2.Dumps group row by row.GA_ITEM into g select new { Type1 = g.Key, Type2 = g.ToList() });
Debug.Print("::::try2:::::");
foreach (var row in try2)
{
Debug.Print(row.Type1.ToString());
Debug.Print(row.Type2.ToString());
}
I'm converting an Access SQL query to Linq. The two columns I am selecting from my table Dumps are GA_ITEM and REM_QTY. My try1 is working out just fine and I see the contents of both columns printed out. My try1 is not yet duplicating the functionality of the Access SQL query.
My try2 is an attempt at grouping. For my try2 row.Type1.ToString() is readable however row.Type2.ToString() is showing up in the output window as:
System.Collections.Generic.List`1[garminaspsandbox3.Models.Dump]
What I really would like to do is in try2 select GA_ITEM and REM_QTY like I did in try1 and group by GA_ITEM however those fields aren't showing up in my autocomplete for the g object.
Does anyone know how to do this in Linq?
Thank you for posting...
Your Type2 property holds a List, not a single item,So you need to use another loop and iterate over the items in that group:
foreach (var row in try2)
{
Debug.Print(row.Type1.ToString());
foreach(var item in row.Type2)
{
Debug.Print(item.GA_ITEM);
Debug.Print(item.REM_QTY);
}
}
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
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"] });
Given the following code:
var filtered = (from a in lijst select a);
foreach (string brandstof in checkedListBoxHoofdbrandstof.CheckedItems)
{
MessageBox.Show(brandstof);
filtered = (from a in lijst where a.Hoofdbrandstof.Contains(brandstof) select a);
}
MessageBox.Show(filtered.Count().ToString());
lijst is a List of a class , with about 16000 items
When checkedListBoxHoofdbrandstof.CheckedItems contains more than 1 item, the query only uses the results from the last where-clause.
For example: I have 2 values, A and B, and despite the fact that A returns 100 rows, and B returns 50 rows, only the last 50 rows are included as a result. A is not included in the results anymore.
I tried using a.Hoofdbrandstof.Any, but that results in an error about types. I also tried a.Hoofdbrandstof.Equals, with the same results.
Does anyone know how I can combine these results, so that both the results from A and B are in var filtered?
The simple way:
var checkedItems = checkedListBoxHoofdbrandstof.CheckedItems;
var filtered = from a in lijst
where checkedItems.Contains(a.Hoofdbrandstof)
select a
But complexity of this method if O(n^2) to reduce it to O(n) use a Join operation
var checkedItems = checkedListBoxHoofdbrandstof.CheckedItems.Cast<string>().ToList();
var filtered = from a in lijst
join checkedItem in checkedItems on a.Hoofdbrandstof equals checkedItem
select a