I have a datatable which holds 3 columns:
Product, Price, Manufacturer
I am trying to read the data into a list of objects, defining which rows are stored by the following:
Store all Products which have the cheapest price and take the
manufacturer from that line.
EG-
Product, Price, Manufacturer
table, 15.00, ikea
table, 12.50, woodpty
chair, 11.00, ikea
chair, 9.00, woodpty
The expected output into the list is two objects with the following properties:
table, 12.50, woodpty
chair, 9.00, woodpty
I have the following code, but I am getting an error-
String does not contain a definition for 'Name' and no extension
method 'Name' accepting a first argument of type 'string' could be
found (are you missing an assembly reference?)
var result = (
from row in dtProductListings.AsEnumerable()
group row by row.Field<string>("Product") into g
let x = new
{
Name = g.Key.Name, //THIS LINE IS CAUSING THE PROBLEM
Price = g.Min(x => x.Field<float>("Price"))
}
where (row.Name == x.Name && row.Price == x.Price)
select new Foo
{
Name = row.Name,
Manufacturer = row.Manufacturer,
Price = row.Price
}
).ToList();
I am still quite new to LINQ and am wondering where I am going wrong? Any help would be greatly appreciated.
Nice try but your attempt has a few issues. Firstly where you have used Name = g.Key.Name should be Name = g.Key, and secondly your Linq expression will fail to compile also because row is no longer in scope after the group by clause.
Linq can be a tricky thing to get your head around at the start, but what you're effectively trying to achieve is to group the rows by the product column and then select the row in each group which has the lowest price.
So to create the product groups:
var rowsGroups = from row in dtProductListings.AsEnumerable()
group row by row.Field<string>("Product") into g
select g.OrderBy(row => row.Price);
For your example, this will produce two groups of IOrderedEnumerable based on the product values, with the items in each group being order by lowest price to highest price.
Group 1: Key = "table"
Row 1: table, 12.50, woodpty
Row 2: table, 15.00, ikea
Group 2: Key = "chair"
Row 1: chair, 9.00, woodpty
Row 2: chair, 11.00, ikea
So now all your have to do to get your result is to select the first item in each group to get the minimum priced item:
var result = (from row in rowGroups
select row.First())
.ToList();
The same query using lambda expression and linq chain methods (which I find easier to write since it focuses you on the inputs and outputs of what your're doing):
var result = dtProductListings.AsEnumerable()
.GroupBy(row => row.Field<string>("Product"))
.Select(x => x.OrderBy(y => y.Price))
.Select(x => x.First())
.ToList();
Simplified further:
var result = dtProductListings.AsEnumerable()
.GroupBy(row => row.Field<string>("Product"))
.Select(x => x.OrderBy(y => y.Price).First())
.ToList();
Try this:
var result = (
from row in dtProductListings.AsEnumerable()
group row by row.Field<string>("Product") into g
select new
{
Name = g.Key,
Price = g.Min(x => x.Field<float>("Price"))
Manufacturer = g.First().Field<string>("Manufacturer")
}
).ToList();
Related
Very new with LINQ here.
I have the following data in my table (TableA):
ID Name SubmissionNo
1 Jim A-1
2 Andy A-2
3 Rick A-2
4 Mary A-3
5 Zim A-4
6 Loren A-1
I then need to create a query which will allow me to get from that table, those records which have duplicate submission numbers.
Here's my solution so far (Context is the database context):
var duplicates = (from tbl in Context.TableA.AsNoTracking()
group tbl by tbl.SubmissionNo into grp
select new { count = grp.Count(), submissionNo = grp.Key})
.Where(x => x.count > 1)
.OrderBy(y => y.submissionNo).ToList();
The variable duplicates then contains the record:
count submissionNo
2 A-1
2 A-2
I then write the main query which will allow me to get all the records from TableA which has duplicate submissionNo
var myList = (from tbl in Context.TableA.AsNoTracking()
join dup in duplicates on tbl.SubmissionNo equals dup.submissionNo
select new
{
ID = tbl.ID,
Name = tbl.Name,
SubmissionNo = tbl.SubmissionNo
})
.ToList();
I am then getting an error for the myList query with
Unable to create a constant value of type 'Anonymous Type'. Only primitive types or enumeration types are supported in this context.
I think there must be a better way to do this as from the TableA above, I practically want the following results:
ID Name SubmissionNo
1 Jim A-1
2 Andy A-2
3 Rick A-2
6 Loren A-1
Your first query, slightly modified, has all information you need:
var myList = from tbl in Context.TableA.AsNoTracking()
group tbl by tbl.SubmissionNo into grp
where grp.Count() > 1
from item in grp
select new
{
count = grp.Count(),
submissionNo = grp.Key,
item.Name,
);
The pattern group into grp - from item in grp is a commonly used query pattern to group items and then flatten the group again, while keeping in touch with the group data (like Count() and Key).
Now you don't need the join anymore and the exception doesn't occur. By the way, the exception tells you that EF can only handle joins with collections of primitive types (int etc.), because it has to translate the whole expression into SQL. There's simply no translation for rich objects like TableA.
By the way, the query can be improved by removing the repeated Count():
var myList = from tbl in Context.TableA.AsNoTracking()
group tbl by tbl.SubmissionNo into grp
let count = grp.Count()
where count > 1
from item in grp
select new
{
count = count,
submissionNo = grp.Key,
item.Name,
);
This will generate a more efficient SQL statement containing one COUNT instead of two.
Since Entity Framework does not support joining in-memory collections of objects with database collections, a common workaround for this is to filter using Contains.
First, you need to get the IDs to filter on:
var duplicates = (from tbl in Context.TableA.AsNoTracking()
group tbl by tbl.SubmissionNo into grp
select new { count = grp.Count(), submissionNo = grp.Key})
.Where(x => x.count > 1)
.OrderBy(y => y.submissionNo)
.ToList();
var duplicateIds = duplicates.Select(x => x.submissionNo).ToList();
And then change your query to perform a WHERE...IN instead of a JOIN:
var myList = (from tbl in Context.TableA.AsNoTracking()
where duplicateIDs.Contains(tbl.SubmissionNo)
select new
{
ID = tbl.ID,
Name = tbl.Name,
SubmissionNo = tbl.SubmissionNo
})
.ToList();
I have about 20000 rows data in Table in db.
I want to get 3 rows data in this table , but I don't want to get same rows.
Example code:
var SampleTable = (from v in db.Article
select new Item
{
Name = v.Name
});
var distinctItems = SampleTable.GroupBy(x => x.Name ).Select(y => y.First());
var threeItem = distinctItems.Take(3).ToList();
If I use var distinctItems = SampleTable.GroupBy(x => x.Name ).Select(y => y.First()); and get 3 rows.
It work, but large data rows will take a large time to do GroupBy.
But if I take 3 rows in SampleTable first, then remove same rows.
If remove rows happen that will cause my rows count not
meet the requirements count.
Have a better way to do?
Let's say I have a table that holds shipping history. I'd like to write a query that counts the amount of shipments per user and gets the shipping name from the most recent entry in the table for that user.
Table structure for simplicity:
ShipmentID
MemberID
ShippingName
ShippingDate
How do I write a LINQ C# query to do this?
It sounds like might want something like:
var query = from shipment in context.ShippingHistory
group shipment by shipment.MemberID into g
select new { Count = g.Count(),
MemberID = g.Key,
MostRecentName = g.OrderByDescending(x => x.ShipmentDate)
.First()
.ShipmentName };
Not really a LINQ answer, but personally, I'd be dropping to SQL for that, to make sure it isn't doing any N+1 etc; for example:
select s1.MemberID, COUNT(1) as [Count],
(select top 1 ShippingName from Shipping s2 where s2.MemberID = s1.MemberID
order by s2.ShippingDate desc) as [LastShippingName]
from Shipping s1
group by s1.MemberID
You can probably do LINQ something like (untested):
var qry = from row in data
group row by row.MemberId into grp
select new {
MemberId = grp.Key,
Count = grp.Count(),
LastShippingName =
grp.OrderByDescending(x => x.ShippingDate).First().ShippingName
};
I have four columns name SrNo,RollNo,Name,Age in my datatable and corresponding values as
SrNo ,Roll No,Name,Age
1, 1, ABC, 20
2, 2, DEF, 22
3, 3, ABC, 25
I want search how many different a names are present & their count.
Please suggest
Thanks
The simplest way to do this would probably be with LINQ (IMO, anyway):
var groups = table.AsEnumerable()
.GroupBy(x => x.Field<string>("Name"))
.Select(g => new { Name = g.Key, Count = g.Count() });
That's assuming you really do have the data in a DataTable. If it's actually still in the database, you can use a similar LINQ to SQL query:
var groups = dataContext.GroupBy(x => x.Name)
.Select(g => new { Name = g.Key, Count = g.Count() });
Actually you could use an overload of GroupBy to do it all in one method call:
var groups = dataContext.GroupBy(x => x.Name,
(key, group) => new { Name = key,
Count = group.Count() });
select count(1) as cnt, Name from mytable group by Name
Write a SQL query that creates this summary and execute it using ADO.NET.
If you want to use sql server. Below is the answer
Select Name, count(Name)
From YourTableNamew
Group by Name
SELECT COUNT(DISTINCT column_name) FROM table_name group by column_name
Using Ado.Net Entity framework, I am trying to get the 'top 3' items in a table based on the amount of times they appear in a table.
For example:
Table:
basket_to_product_id | basket_id | product_id
I want to see how many times product_id occurs, and would like to return the top 3 product_ids that occur the most frequently.
I'm stuck at:
List<BasketToProduct> btplist = entities.BasketToProduct. ..........?
Something like this should work (of course I do not know the actual names of your properties):
IEnumerable<int> top3ProductIds = (from btp in entities.BasketToProduct
group btp by btp.ProductId into g
orderby g.Count() descending
select g.Key).Take(3);
You could try to use a LINQ query on the table.
Try this:
var query = entities.BasketToProduct
.GroupBy(btp => btp.ProductID)
.Select(g => ProductID = g.Key, Count = g.Count())
.OrderBy(g => g.Count)
.Take(3);
It'll get you the top three ProductIDs and their associated counts.