LINQ Sum with GroupBy - c#

(Not sure if I even need GroupBy)
My (simplified) tables:
Products (ProductID, Name, Code)
Invoices (InvoiceID, Number, IsPaid)
Invoices_Products (InvoiceID, ProductID, Quantity, Price) - the many-to-many linking table
I need to show a list of Invoices_Products of paid Invoices grouped by the Product Code which sums (Quantity*Price).
The code that I first use to get a collection that I can bind to the UI:
IEnumerable<Invoices_Products> invoices_products = db.Invoices_Products
.Where(ip => ip.Invoice.IsPaid).DistinctBy(m => m.Product.Code);
I then iterate through this to bind it to the UI:
List<BindableInvoiceProduct> bindableInvoiceProducts =
new List<BindableInvoiceProduct>();
foreach (var item in invoices_products)
{
decimal salesValue = db.Invoices_Products.Where(ip => ip.Invoice.IsPaid
&& ip.Product.Code == item.Product.Code).Sum(m => (m.Price * m.Quantity));
bindableInvoiceProducts.Add(new BindableInvoiceProduct()
{
A = item.A,
B = item.B,
SalesValue = salesValue.ToString()
});
}
(The DistinctBy method there is from morelinq)
Why does this not total correctly?
edit:
Some data:
Product - ProductID = 1, Name = 123, Code = A
Product - ProductID = 2, Name = 456, Code = A
Invoice - InvoiceID = 1, Number = INV123, IsPaid = True
Invoices_Products - InvoiceID = 1, ProductID = 1, Quantity = 10, Price = 100
Invoices_Products - InvoiceID = 1, ProductID = 2, Quantity = 10, Price = 200
Expected result:
Code = A, SalesValue = 3000

from invoice in invoices
where invoice.IsPaid
from xr in invoice.InvoiceProducts
group xr.Quantity * xr.Price by xr.Product.Code into g
select new {Code = g.Key, SalesValue = g.Sum()};
If you want per invoice, then:
from invoice in invoices
where invoice.IsPaid
from xr in invoice.InvoiceProducts
group xr.Quantity * xr.Price
by new {Code = xr.Product.Code, Invoice = invoice }
into g
select new {
Code = g.Key.Code,
Invoice = g.Key.Invoice,
SalesValue = g.Sum()};

Based on your description I would have written:
var bindableInvoiceProducts = db.Invoices_Products
.Where(ip => ip.Invoice.IsPaid)
.GroupBy(ip => ip.Product.Code,
(code, ips) => new BindableInvoiceProduct()
{
Code = code,
SalesValue = ips.Sum(ip => (ip.Price*ip.Quantity))
})
.ToList();
Is that, what you need? What is item.A and item.B in your code?

Related

Calculating product's quantity with nested EF Query

I want to calculate the quantity of every product. I want to use these tables for this.
I want take result like this.
StockId | StockName | Sum(ProductNumber) [actually product quantity]
I tried this code for can take right result but It is returning null value even though it is the condition that satisfies the conditions.
CimriContext context = new CimriContext();
public ICollection<StockDto.StockHeader> FillDataGrid(int userCompanyId)
{
ICollection<StockDto.StockHeader> Stocks = context.Stocks.Where(c => c.UserCompanyId==userCompanyId).
Select(c => new StockDto.StockHeader()
{
StockId = c.StockId,
StockName = c.StockName,
Piece=0
}).ToList();
ICollection<StockDto.StockHeader> ProductHeader = new List<StockDto.StockHeader>();
foreach (var products in Stocks)
{
products.Piece = context.ProductTransactions.Where(p => p.StockId==products.StockId).Sum(s => s.ProductNumber);
ProductHeader.Add(products);
}
return ProductHeader.ToList();
}
You want to retrieve ProductCount for per stock record. You can perform it by joining two tables and grouping StockId and StockNumber like this;
var query = from stocks in context.Stocks
join products in context.ProductTransactions ON stocks.StockId equals products.StockId
where stocks.UserCompanyId = userCompanyId
group stocks by new
{
stocks.StockId,
stocks.StockName
} into gcs
select new
{
StockId = gcs.Key.StockId,
StockName = gcs.Key.StockName,
ProductCounts = gcs.Count()
};

Add duplicates together in List

First question :)
I have a List<Materiau> (where Materiau implements IComparable<Materiau>), and I would like to remove all duplicates and add them together
(if two Materiau is the same (using the comparator), merge it to the first and remove the second from the list)
A Materiau contains an ID and a quantity, when I merge two Materiau using += or +, it keeps the same ID, and the quantity is added
I cannot control the input of the list.
I would like something like this:
List<Materiau> materiaux = getList().mergeDuplicates();
Thank you for your time :)
Check out Linq! Specifically the GroupBy method.
I don't know how familiar you are with sql, but Linq lets you query collections similarly to how sql works.
It's a bit in depth to explain of you are totally unfamiliar, but Code Project has a wonderful example
To sum it up:
Imagine we have this
List<Product> prodList = new List<Product>
{
new Product
{
ID = 1,
Quantity = 1
},
new Product
{
ID = 2,
Quantity = 2
},
new Product
{
ID = 3,
Quantity = 7
},
new Product
{
ID = 4,
Quantity = 3
}
};
and we wanted to group all the duplicate products, and sum their quantities.
We can do this:
var groupedProducts = prodList.GroupBy(item => item.ID)
and then select the values out of the grouping, with the aggregates as needed
var results = groupedProducts.Select( i => new Product
{
ID = i.Key, // this is what we Grouped By above
Quantity = i.Sum(prod => prod.Quantity) // we want to sum up all the quantities in this grouping
});
and boom! we have a list of aggregated products
Lets say you have a class
class Foo
{
public int Id { get; set; }
public int Value { get; set; }
}
and a bunch of them inside a list
var foocollection = new List<Foo> {
new Foo { Id = 1, Value = 1, },
new Foo { Id = 2, Value = 1, },
new Foo { Id = 2, Value = 1, },
};
then you can group them and build the aggregate on each group
var foogrouped = foocollection
.GroupBy( f => f.Id )
.Select( g => new Foo { Id = g.Key, Value = g.Aggregate( 0, ( a, f ) => a + f.Value ) } )
.ToList();
List<Materiau> distinctList = getList().Distinct(EqualityComparer<Materiau>.Default).ToList();

Using Linq to extract data from a Table

I have a table as shown in the picture below. I need to extract a list from the table where it lists it on the basis of the country. And there is no repetition of the country. So for example, China would appear only once, its total pay would be added, and its total Debt would be added and all other column would be added respectively. So It should look like something below:
Country --- Totay Pay---Total Debt--- NetAmount
China-- (3.1+4.2)B--- (2.0+2.0)B----(8+4)B
India--
Russia---
I would prefer if we can use LINQ expressions. I tried using GroupBy and other operators but can't get my head around.
GroupBy operator is good direction - you should group by country and sum other values using one of its overloads that:
Groups the elements of a sequence according to a specified key selector function and creates a result value from each group and its key.
Assuming data like:
var data = new List<Data>()
{
new Data() { Country = "China", Index = 2, TotalPay = 3.1M, TotalDebt = 2.0M, NetAmount = 8.0M},
new Data() { Country = "India", Index = 4, TotalPay = 2.1M, TotalDebt = 3.0M, NetAmount = 9.0M},
new Data() { Country = "China", Index = 8, TotalPay = 5.1M, TotalDebt = 4.0M, NetAmount = 10.0M},
};
you can write:
var results = data.GroupBy(x => x.Country, (key, elements) => new Data()
{
Country = key,
Index = elements.Sum(v => v.Index),
TotalPay = elements.Sum(v => v.TotalPay),
TotalDebt = elements.Sum(v => v.TotalDebt),
NetAmount = elements.Sum(v => v.NetAmount)
});

Linq query to sum values from tables

I have a LINQ query, which takes an invoice, looks up the quantity and unit price of each item attached, and in another linked table, looks up any payments made against the invoice.
Currently this is:
from i in Invoices
select new
{
i.InvoiceId, i.CustomerName,
Items =
from it in InvoiceItems
where i.InvoiceId == it.InvoiceId
select new {
it.Item,
it.Quantity,
it.UnitPrice,
SubPrice=it.Quantity*it.UnitPrice
},
Payments =
from pi in PaymentInvoices
where i.InvoiceId == pi.InvoiceId
select new {
SumPayments=pi.AmountAllocated
}
}
This shows the following results:
How do I change this LINQ query to show just the sum of SubPrice and SumPayments (ie:
Invoice 11: SubPrice= 90.00 SumPayments= 24.00
Invoice 12: SubPrice=175.00, SumPayments=175.00
Thank you for any help,
Mark
UPDATE SHOWING WORKING ANSWER FOLLOWING ANSWER FROM KENNETH
from i in Invoices
select new
{
InvoiceID = i.InvoiceId,
CustomerName = i.CustomerName,
SubPrice = InvoiceItems.Where(it => it.InvoiceId == i.InvoiceId).Select(it => it.Quantity*it.UnitPrice).Sum(),
SumPayments = PaymentInvoices.Where(pi => pi.InvoiceId == i.InvoiceId).Select(pi => pi.AmountAllocated).Sum()
}
You could use the following method:
from i in Invoices
select new
{
InvoiceID = i.InvoiceId,
CustomerName = i.CustomerName,
SubPrice = InvoiceItems.Where(it => it.InvoiceID = i.InvoiceID).Select(it => it.Quantity*it.UnitPrice).Sum(),
SumPayments = PaymentInvoice.Where(pi => pi.InvoiceId = i.InvoiceId).Select(pi => pi.AmountAllocated).Sum()
}

Multiple group by and Sum LINQ

I have a products sales table that looks like this:
saleDate prod qty
10/22/09 soap 10
09/22/09 pills 05
09/25/09 soap 06
09/25/09 pills 15
I need to make the SUM of each MONTH so the final table would look like this:
saleDate prod qty
10/09 soap 10
09/09 soap 06
09/09 pills 20
Can I do this with LINQ?
var products = new[] {
new {SaleDate = new DateTime(2009,10,22), Product = "Soap", Quantity = 10},
new {SaleDate = new DateTime(2009,09,22), Product = "Pills", Quantity = 5},
new {SaleDate = new DateTime(2009,09,25), Product = "Soap", Quantity = 6},
new {SaleDate = new DateTime(2009,09,25), Product = "Pills", Quantity = 15}
};
var summary = from p in products
let k = new
{
//try this if you need a date field
// p.SaleDate.Date.AddDays(-1 *p.SaleDate.Day - 1)
Month = p.SaleDate.ToString("MM/yy"),
Product = p.Product
}
group p by k into t
select new
{
Month = t.Key.Month,
Product = t.Key.Product,
Qty = t.Sum(p => p.Quantity)
};
foreach (var item in summary)
Console.WriteLine(item);
//{ Month = 10/09, Product = Soap, Qty = 10 }
//{ Month = 09/09, Product = Pills, Qty = 20 }
//{ Month = 09/09, Product = Soap, Qty = 6 }
var q = from s in sales
group s by new {saleDate = s.saleDate.ToString("MM/yy"), s.prod} into g
select new { g.Key.saleDate, g.Key.prod, qty = g.Sum(s => s.qty) };
class Program
{
static void Main(string[] args)
{
var sales = new List<Sale>();
sales.Add(new Sale() { Product = "soap", saleDate = new DateTime(2009, 10, 22), Quantity = 10});
sales.Add(new Sale() { Product = "soap", saleDate = new DateTime(2009, 9,22), Quantity = 6});
sales.Add(new Sale() { Product = "pills", saleDate = new DateTime(2009,9,25), Quantity = 15});
sales.Add(new Sale() { Product = "pills", saleDate = new DateTime(2009,9,25), Quantity = 5});
var q = from s in sales
group s by new { s.Product, s.saleDate.Month, s.saleDate.Year } into g
select new {Month = String.Format("{0:MM/yy}", new DateTime(g.Key.Year, g.Key.Month, 1)), product = g.Key.Product, quantity = g.Sum(o=>o.Quantity)};
}
}
class Sale
{
public DateTime saleDate { get; set; }
public int Quantity { get; set; }
public string Product { get; set; }
}
Sure.
It'll look like this:
var GroupedSales =
from p in products
group p by p.saleDate.Month into g
select new { saleMonth = g.saleDate.Month, QtySold = g.Sum(p => p.qty) };
See: http://msdn.microsoft.com/en-us/vcsharp/aa336747.aspx#sumGrouped
Also, note that I was operating under the assumption that your dataset only had 2009 data. If you want to group by month/year, you can use saleDate.ToString("yyyyMM") instead of saleDate.Month.

Categories