How to write the following group by and stripping time Linq query? - c#

I have list like this-
Id Date
1 01/03/2011 14:15:23.320
1 01/03/2011 16:15:23.320
1 01/03/2011 18:15:23.320
1 01/04/2011 19:15:23.320
2 01/03/2011 14:15:23.320
2 01/03/2011 15:15:23.320
2 01/03/2011 18:15:23.320
2 01/05/2011 19:15:23.320
2 01/07/2011 20:15:23.320
My desired output is -
Id Date
1 01/03/2011
1 01/04/2011
2 01/03/2011
2 01/05/2011
2 01/07/2011
So how can I group by on the first list on id and take only the date part and achieve the results above in Linq ?

Try something like this:
var result = yourList.GroupBy(item =>
new {Id = item.Id, Date = item.Date.Date});
Basically, DateTime.Date strips the time information.
To loop through the result, you could do:
foreach(var grp in result)
{
Console.WriteLine("{0}: {1}", grp.Key.Id, grp.Key.Date);
}

It appears that you want to group on the date and id, so you want to pull those out into a structure that you can group on. It's when you create this key structure that you can format the date using the Date property to create the grouping correctly:
from item in list
group item by new { item.Id, Date = item.Date.Date } into g
select g
At that point, the keys in the grouping returned will give you the list you want.

Related

unable to draw chart of highest sold products

I have a soldproduct table I want to draw the chart of the top 10 highest sold products.
trying it with following code but I don't know what is the exact problem while, it is logically right.
here is the error
System.ArgumentException: 'DbExpressionBinding requires an input expression with a collection ResultType. Parameter name: input'
DateTime start = startDate.Value;
DateTime end = EndDate.Value;
var TopTen = (from q in db.soldProduct
where (q.addDate >= start && q.addDate <= end)
orderby q.Barcode.Count() descending
select new
{
count = q.Barcode.Count(),
q.productName
}).Take(10);
foreach (var item in TopTen)
{
chartCustomer.Series["Customer"].Points.AddXY(item.productName, item.count);
}
ADDED :- I have in each row the item name and the quantity which might be one or more, I want to add that as well to the query but it is getting harder
model of the bill of soldProduct contains the following
public string barcode {get;set;}
public int productId{get;set;}
public string productName{get;set;}
public int quantity{get;set;}
public DateTime addDate{get;set;}
public double amount{get;set}
so the table if it has the following data
productId
barcode
productName
quantity
addDate
amount
1
111
milk
3
3/2/2020
15
1
111
milk
1
3/2/2020
5
2
222
bread
5
3/2/2020
20
3
333
cheese
1
3/2/2020
100
1
111
milk
3
3/2/2020
15
2
222
bread
3
3/2/2020
15
quantity of the of item sold the highest is bread with 8 occurrences it should come first then milk with 7 occurrences and then cheese.
Addition after comments: Apparently every SoldProduct has a property Amount. You don't want to count the number of SoldProducts, but the total Amount of all SoldProducts.
So if SoldProduct1 for product "Milk" has Amount 10 and SoldProduct[22] for product "Milk" has Amount 14, you want as result that of product "Milk" 24 items have been sold (= 10 + 14).
End addition after comment
So you have a table, SoldProducts. Every row in this table represents one Sale of a Product.
table SoldProducts has a.o. at least the following columns
AddDate: when was the sale
ProductName: the Name of the sold product,
Amount: the number of sold products in this sale.
I want ... the top 10 highest sold products
"highest sold products" probably doesn't have to do anything to do with the height of the sold products, but with the total Amount of products sold.
To calculate this, you need to make groups of SoldProducts with the same name. You'll get groups of the soldProducts with name: "Bread" and SoldProducts with name "Milk", etc.
Then you want to sum the total Amount of all soldProducts per group.
So if group "Bread" has 3 SoldProducts with Amounts 10, 7, 5, then you want as result: ["Bread", 22]. 22 is the total number of Breads sold.
You order the result of the grouping items by descending TotalAmount and take the first 10 items.
To group elements based on something common is done by one of the overloads of Enumerable.GroupBy. In this case, I take the overload with parameter resultSelector to count the number of elements in each group.
DateTime beginDate = ...
DateTime endDate = ...
var topTenSoldProductNames = dbContext.SoldProducts
.Where(soldProduct => beginDate <= soldProduct.AddDate
&& endDate >= soldProduct.AddDate)
// make groups with same ProductName
.GroupBy(soldProduct => soldProduct.ProductName,
// parameter resultSelector: take each ProductName,
// and all SoldProducts with this ProductName to make one new
(productName, soldProductsWithThisProductName) => new
{
Name = productName,
TotalAmount = soldProductsWithThisProductName
.Select(soldProduct => soldProduct.Amount)
.Sum(),
})
// order by descending TotalAmount, and take the first 10
.OrderByDescending(groupResult => groupResult.TotalAmount)
.Take(10);
Your query is wrong, you are returning count of chars. And without grouping this query is useless.
var groupingQuery =
from q in db.soldProduct
where (q.addDate >= start && q.addDate <= end)
group q by q.productName into g
select new
{
productName = g.Key,
count = g.Count(),
};
var TopTen = groupingQuery.OrderByDescending(x => x.count).Take(10);

Count Dates which a specific Customer does not have a Shop in it

Here is an example Data Table:
ID Amount Date
--------------------------
1 3.000 2016/1/1
2 4.000 2016/1/1
1 6.000 2017/1/1
1 3.000 2018/1/1
3 2.000 2019/1/1
I need to count Dates which a specific Customers does not have a Shop in it.
for example ID 2 does not have a shop in 2017/1/1 and 2018/1/1, so the count will be 2. and the count for customer ID 3 will be 3 because He does not have a shop in 2016/1/1 and 2017/1/1 and 2018/1/1
I think I should use grouping but do not know how to count which I want for a specific ID!
orders.GroupBy(x => x.Date)......???
Assuming you have list of objects:
// number of all different dates
var datesCount = list.Select(i => i.Date).Distinct().Count();
int customerId = 2;
//number of dates with customer shopping
var customerDatesCount = list.Where(i => i.ID == customerId).Select(i => i.Date).Distinct().Count();
var nonShoppingDatesCount = datesCount - customerDatesCount;
You need two steps here.
Get all distinct order dates
var allDates = orders.Select(o => o.Date).Distinct();
Find those dates which customer don't have. Except operation will do that
var missingDates = allDates.Except(orders.Where(o => o.ID == 2).Select(o => o.Date));
If you need just number of missing dates, then use missingDates.Count() or (better) use solution by #Roma

How does GroupBy in LINQ work?

I originally have a dictionary of <string, List<ProviderSummary>> called rowsDictionary
Now for each key of that dictionary I group its list of values by some criteria as below:
Dictionary<string, List<ProviderSummary>> providerGroups = rowsDictionary.ToDictionary(
x => x.Key,
v => v.Value.GroupBy(x => new { x.GroupID, x.GroupFin, x.ZipCode })
.Select(x => x.First())
.ToList());
so for example if key["1234"] originally had 6 items in its list of values, now it may have two items based on that grouping. My question and confusion is what happens to the rest of the values? ( those four) and what values will go in to these two lists that are returned for the group?
Group by works by taking whatever you are grouping and putting it into a collection of items that match the key you specify in your group by clause.
If you have the following data:
Member name Group code
Betty 123
Mildred 123
Charli 456
Mattilda 456
And the following query
var query = from m in members
group m by m.GroupCode into membersByGroupCode
select membersByGroupCode;
The group by will return the following results:
You wouldn’t typically want to just select the grouping directly. What if we just want the group code and the member names without all of the other superfluous data?
We just need to perform a select to get the data that we are after:
var query = from m in members
group m by m.GroupCode into membersByGroupCode
let memberNames = from m2 in membersByGroupCode
select m2.Name
select new
{
GroupCode = membersByGroupCode.Key,
MemberNames = memberNames
};
Which returns the following results:
 
What values will go in to the lists that are returned for the group?
The first for each group, because you do:
.Select(x => x.First())
What happens to the rest of the values?
They will not be projected into your target dictionary.
Your LINQ group by query takes the original list, performs additional grouping on it, and then prunes the list based on that grouping.
Consider a situation where a single list contains these items:
GroupID GroupFin ZipCode Name
------- -------- ------- ----
1 1 94111 A
1 1 94111 B
1 1 94111 C
1 1 94111 D
1 2 94110 E
1 2 94110 F
Group by would make two groups out of this list of six:
GroupID=1 GroupFin=1 ZipCode=94111
GroupID=1 GroupFin=2 ZipCode=94110
The first group would contain providers A, B, C, and D; the second group would contain E and F.
The next thing that your query does is applying First. This operation picks the initial item from the group; in this case, it would be A and E. The remaining items are thrown ignored.

C# List querying and grouping

I have this list:
OrderId ProductId DateTime
1 1 10.01.2012
1 2 09.01.2012
2 1 11.01.2012
3 1 12.01.2012
3 2 13.01.2012
I want to extract another another List from this that's only ProductId==1 and DateTime is 10.01.2012.
i.e. only productId==1 for every OrderId's.
Also I only want the least dateTime version of that item.
So for the above list, 10.01.2012 is the least dateTime where the productId==1.
Result Table;
OrderId ProductId DateTime
1 1 10.01.2012
how can I do this?
Here is a possible solution:
myList = myList.Where(x.ProductId == 1).OrderBy(x => x.DateTime);
That will order your new list by the DateTime value where the productId is equal to 1. If you want to get only the first one, you can use the .First() method of the list, e.g:
myCustomType = myList.Where(x => x.ProductId == 1).OrderBy(x => x.DateTime).First();

LINQ: Selecting items from a list (Group By/Select/Sum & Max!)

Just getting my head around Linq and having lots of fun! Can any one aid me with a query for this:
I have a list of data:
Key Value
Aaa 12
AaA 10
AAa 5
BBB 2
Bbb 1
1. I want to group by Key.ToUpper()
2. For every group I need the Max(Value) & Sum(Value)
3. For every group I want to select the entries
There the Value != Max(value)
the final result should be like this:
Key Max Total
AaA 12 27
AAa 12 27
Bbb 2 3
Thanks!
Update, actually I also need the Key from the Maximum entry:
Key Max Total Correct
AaA 12 27 Aaa
AAa 12 27 Aaa
Bbb 2 3 BBB
:)
var results =
from kvp in source
group kvp by kvp.Key.ToUpper() into g
select new
{
Group = g,
Max = g.Max(kvp => kvp.Value),
Total = g.Sum(kvp => kvp.Value)
} into ag
from x in ag.Group //SelectMany
where x.Value != ag.Max
//for the update to the question - note: possibly ambiguous
let correct = ag.Group.Where(y => y.Value == ag.Max).First().Key
select new
{
Key = x.Key,
Max = ag.Max,
Total = ag.Total,
Correct = correct
};
I kinda like the question because of all the little parts (some are rarely used) that are required to make the answer.
Max = g.Max(kvp => kvp.Value),
Total = g.Sum(kvp => kvp.Value)
Performing multiple aggregations on a group is straightforward, yet challenging if you don't know how.
select a into b
This clause takes everything that happened before and starts a new query with the target. Without it, I'd have to start a new query like this:
var A = ... select a
var B = from b in A
It's important to note that the select into clause removes kvp and g from scope.
from b in source
from a in b.A //SelectMany
This "unpacking" of the child collection turns my query about b's into a query about a's. Unlike the default Enumerable.SelectMany overload, it leaves the parent (b) in scope.
where x.Value != ag.Max
Comparing a child's property with a parent's property? Delightful. It's important to remember to break out where anytime you want to filter, even if you just grouped (there is no HAVING).

Categories

Resources