How to aggregate over one property while summing another? - c#

I have a list of invoices and each record consist of a customer ID and an amount. The aim is to produce a list of payments where each payment is unique per customer (there might be multiple invoices per each customer) and sums the related invoices' amounts to a total.
Producing a list of distinct invoices (with respect to the customer ID) is very easy. The things is that I then only have the value of the first invoice's amount and not the sum.
List<Payment> distinct = invoices
.GroupBy(invoice => invoice.CustomerId)
.Select(group => group.First())
.Select(invoice => new Payment
{
CustomerId = invoice.CustomerId,
Total = invoice.Amount
}).ToList();
Is there a smooth LINQ-fu for that or do I need to go foreach on my list?

If you have something like this
Invoice[] invoices = new Invoice[3];
invoices[0] = new Invoice { Id = 1, Amount = 100 };
invoices[1] = new Invoice { Id = 1, Amount = 150 };
invoices[2] = new Invoice { Id = 2, Amount = 300 };
Then you could have your goal as
var results = from i in invoices
group i by i.Id into g
select new { Id = g.Key, TotalAmount = g.Sum(i => i.Amount)};

Based on the answer of jmelosegui:
List<Payment> distinct = invoices
.GroupBy(c => c.CustomerId)
.Select(c => new Payment
{
CustomerId = c.Key,
Total = c.Sum(x => x.Amount)
}).ToList();

Related

GroupBy item and Sum Quantities of items

I have an array of orders within each order is an array of items. How do I group all the orders by item name and get the sum total of items ordered. In this case output would be :
Output
Item01 : quantity = 2;
Item02 : quantity = 45;
GetOrders
public Order[] GetOrders()
{
Order[] orders = new Order[]
{
new Order
{
id = 1,
orderLines = new OrderLine[]
{
new OrderLine
{
itemName = "Item 01",
quantity = 1
},
new OrderLine
{
itemName = "Item 02",
quantity = 3
},
},
},
new Order
{
id = 2,
orderLines = new OrderLine[]
{
new OrderLine
{
itemName = "Item 01",
quantity = 1
},
new OrderLine
{
itemName = "Item 02",
quantity = 42
}
}
}
};
...
I tried the following:
foreach (var order in orders)
{
foreach (var orderline in order.orderLines.GroupBy(x => x.itemName).Select(group => new
{
Metric = group.Key,
Count = group.Count()
}))
{
Console.WriteLine("{0} {1}", orderline.Metric, orderline.Count);
}
}
but it just returns 1 for each item. I am relatively new to programming , so be easy on me.Thanks
To get the sum total of all items ordered, use the following query:
var results =
(from order in orders
from orderLine in order.orderLines
group orderLine by orderLine.itemName into orderLineGrouping
let totalQuantity = orderLineGrouping.Sum(ol => ol.quantity)
select new { itemName = orderLineGrouping.Key, metric = totalQuantity }).ToList();
results.ForEach(resultItem => Console.WriteLine("{0} {1}", resultItem.itemName, resultItem.metric);
Flatten orderlines
group the item name and quantities for output string
Select the output string using grouped key and sum of grouped quantities
See code:
var output = orders.SelectMany(x => x.orderLines)
.GroupBy(x => x.itemName, x => x.quantity)
.Select(x => $"{x.Key} : quantity = {x.Sum(y => y)}");
Flatten OrderLines
You need to familiarise your self with Enumerable.SelectMany its one of the most useful methods around
SelectMany, from LINQ, collapses many elements into a single
collection. The resulting collection is of another element type. We
specify how an element is transformed into a collection of other
elements.
var summary = orders.Where(x => x.OrderLines != null) // Check for null as there seems to be null orderlines in your model
.SelectMany(x => x.OrderLines) // Flatten
.GroupBy(x => x.itemName) // Group
.Select(group => new // Project
{
ItemName = group.Key,
TotalQuantity = group.Sum(x => x.quantity)
})
.ToList(); // To List
Tip : use appropriate casing for itemName and quantity
Capitalization Conventions
The following table summarizes the capitalization rules for
identifiers and provides examples for the different types of
identifiers.
Sorry my OCD just kicked in

LINQ multiple group by and then getting the first group by value count

I have a linq query like followin:
var _transactionsList = TransactionsData
.GroupBy(x => new { x.ItemID, x.Title, x.GalleryURL })
.Select(pr => new TransactionsTabResults
{
ItemID = pr.Key.ItemID,
Title = pr.Key.Title,
GalleryURL = pr.Key.GalleryURL,
ItemPrice = pr.OrderByDescending(a => a.TransactionDate).First().ItemPrice,
TotalSoldItems = pr.Count(),
TotalRevenuePerItem = pr.Sum(y => y.ItemPrice),
AveragePrice = pr.Average(y => y.ItemPrice),
}).ToList();
I'm trying to fetch the total sold items value by grouping it by like this:
ItemID Sales ItemName
1 1 Item1
1 3 Item1
1 5 Item1
1 6 Item1
2 2 Item2
2 2 Item2
2 2 Item2
2 2 Item2
The desired output would be:
ItemID Sales ItemName
1 15 Item1
2 8 Item2
The query above that I wrote gives me wrong values for total sales by saying:
TotalSoldItems = pr.Count(),
How can I count, or sum all the sales of one Item which has unique ID(this is what I'm grouping by)...
What am I doing wrong??
You are using GroubBy wrong way. You create new unique object every time. So your .GroupBy(x => new { x.ItemID, x.Title, x.GalleryURL }) and .Select(x => new { Key = new { x.ItemID, x.Title, x.GalleryURL}, Value =x }) means the same
If you need unique Id then group by Id only
TransactionsData
.GroupBy(x => x.ItemID)
.Select(pr => new TransactionsTabResults
{
ItemID = pr.Key,
Title = pr.First().Title,
GalleryURL = pr.First().GalleryURL,
ItemPrice = pr.OrderByDescending(a => a.TransactionDate).First().ItemPrice,
TotalSoldItems = pr.Count(),
TotalRevenuePerItem = pr.Sum(y => y.ItemPrice),
AveragePrice = pr.Average(y => y.ItemPrice),
}).ToList();
Advice
Optimize your LINQ. You are iterating through collections many times. This is suggested code:
TransactionsData
.GroupBy(x => x.ItemID)
.Select(pr =>
{
var items = x.pr.ToArray;
var sum = items.Sum(y => y.ItemPrice);
return new TransactionsTabResults
{
ItemID = pr.Key,
Title = items[0].Title,
GalleryURL = items[0].GalleryURL,
ItemPrice = pr.Aggregate((max, cur)=>max.TransactionDate<cur.TransactionDate?cur:max).ItemPrice,
TotalSoldItems = items.Length,
TotalRevenuePerItem = sum,
AveragePrice = sum/items.Length,
};
}).ToList();

how to get data in monthly wise by group the month and union using joins in linq

Here unoinColumn is return the all the date with out duplicate but i want to get the date group by month
month totalamount
7/2014 10000
8/2014 10000
enter code here var unoinDateColumn = (from agent in db.collections
where agent.Date_Time.Value.Year == thisYear
select agent.Date_Time).Union(from u in db.bank_deposit
where u.Date_Time.Value.Year == thisYear
select u.Date_Time).ToList();
You can do something like that, if I understood well.
Filter collections and deposits by given year, select date_time and amount
Union, group by month, select date in the desired format, and sum amount.
You can of course do all that in one query, but I'm not sure this will be easier to read ;)
var collections = db.Collections.Where(x => x.Date_Time.Value.Year == thisYear)
.Select(m => new {
dt = m.Date_Time.Value,
amount = m.Amount
});
var deposits = db.Collections.Where(x => x.Date_Time.Value.Year == thisYear)
.Select(m => new {
dt = m.Date_Time.Value,
amount = m.Amount
});
var result = collections.Union(deposits)
.GroupBy(m => m.dt.Month)
.Select(g => new {
date = g.First().dt.ToString("MM/yyyy"),
totalAmount = g.Sum(x => x.amount)
});

LINQ to Entities - WHERE clause to exclude Partially Shipped Orders in my example

I have an Order table. An Order can have multiple items in it, with each item shipping at different times. I want to get the list of all Orders which excludes partially shipped or orders. In other words, I need to get the list of all orders that are completely shipped. I may know how to do this in T-SQL. But I'm trying to accomplish this with LINQ-to-Entities (EF4/.Net 4.0/C#).
Consider the following Data:
PK OrderID Item Status
1 00001 TV Shipped
2 00001 TABLET Shipped
3 00002 BLURAYPL Not Shipped
4 00002 MOBILEPH Shipped
5 00002 XBOX Shipped
6 00003 PAPER Shipped
7 00003 PENCIL Shipped
The goal is to get 00001 and 00003 as output.
Here is what I have so far, obviously simplified :
using (MyDBEntities dbcontext = new MyDBEntities())
{
var WashingtonClients = from a in dbcontext.CustomerMasterTable
where a.City == "Washington"
select a.CustomerID;
var ShippedOrdersToWashingtonClients = from o in dbcontext.OrderDetail
where WashingtonClients.Contains(o.CustomerID)
&& o.Status.ToUpper() == "SHIPPED"
//how to exclude Partially Shipped orders here???
select o.OrderID;
}
How to frame the second query in such a way that it excludes orders that have even a single unshipped item in it? Many thanks for your time.
Let's suppose that you have fake list with your data:
var orderDetails = new List<OrderDetail>
{
new OrderDetail{ ID = 1, Item = "TV", OrderID = "00001", Status = "Shipped"},
new OrderDetail{ ID = 2, Item = "TABLET", OrderID = "00001", Status = "Shipped"},
new OrderDetail{ ID = 3, Item = "BLURAYPL", OrderID = "00002", Status = "NotShipped"},
new OrderDetail{ ID = 4, Item = "MOBILEPH", OrderID = "00002", Status = "Shipped"},
new OrderDetail{ ID = 5, Item = "XBOX", OrderID = "00002", Status = "Shipped"},
new OrderDetail{ ID = 6, Item = "PAPER", OrderID = "00003", Status = "Shipped"},
new OrderDetail{ ID = 7, Item = "PENCIL", OrderID = "00003", Status = "Shipped"}
};
Then your linq query should look like this:
var result = orderDetails
.GroupBy(o => o.OrderID)
.Where(g => g.All(i => i.Status == "Shipped"))
.Select(g => g.Key);
so that you get a couple of string - "00001" and "00003" as a result.
Hence for real query to db you can write something like this:
dbContext.OrderDetails
.Where(o => WashingtonClients.Contains(o.CustomerID))
.GroupBy(o => o.OrderID)
.Where(g => g.All(i => i.Status == "Shipped"))
.Select(g => g.Key);

How can I sort by a column that is included in a new{} section of a LINQ query?

The following uses the Northwind database and lists out product groups with four columns: lowest, highest, average, and total price.
How can I sort by total price?
var products = from product in Products
group product by product.Supplier into groupedProducts
//orderby groupedProducts.Key.TotalPrice;
select new
{
groupedProducts.Key.CompanyName,
LowestPrice = groupedProducts.Min(p => p.UnitPrice),
HighestPrice = groupedProducts.Max(p => p.UnitPrice),
AveragePrice = groupedProducts.Average(p => p.UnitPrice),
TotalPrice = groupedProducts.Sum(p => p.UnitPrice)
};
//order by groupedProducts.Key.TotalPrice;
This should work:
var products = (from ... ).OrderBy(item => item.TotalPrice);
How about this?
var products = from product in Products
group product by product.Supplier into groupedProducts
let totalPrice = groupedProducts.Sum(p => p.UnitPrice)
orderby totalPrice
select new
{
groupedProducts.Key.CompanyName,
LowestPrice = groupedProducts.Min(p => p.UnitPrice),
HighestPrice = groupedProducts.Max(p => p.UnitPrice),
AveragePrice = groupedProducts.Average(p => p.UnitPrice),
TotalPrice = totalPrice
};

Categories

Resources