简体   繁体   English

如何使用LINQ TakeWhile和Join到列表

[英]how to use LINQ TakeWhile and Join to a list

I want to map oldest possible credit invoice's date to a list from sale table. 我想将最早可能的信用发票日期映射到销售表中的列表。 I have a list in following manner. 我有以下方式列表。

var tb = // Some other method to get balances.
cust    balance
1           1000
2           2000
3           3000
... so on ...

These balance are accumulation of few invoices or sometimes may be one invoice. 这些余额是少量发票的积累或有时可能是一张发票。

public class Sales
{
    public int id { get; set; }
    public DateTime saleDate { get; set; }
    public int? cust { get; set; }
    public decimal invoiceValue { get; set; }
    // Other properties...
}

Sample data for cust 1 cust 1的样本数据

saleInvNo  saleDate     cust invoiceValue 
1          2018/12/01   1   500
12         2018/12/20   1   750

If I fetch now, report should be as follows. 如果我现在提取,报告应如下。

cust    balance     balanceFromDate
1          1000     2018/12/01 // balance from this onwards.
2          2000     ???
3          3000     ???

Is there easy way to achieve this through LINQ. 有没有简单的方法通过LINQ实现这一目标。

I tried with TakeWhile but attempt was not successful. 我试过TakeWhile,但尝试没有成功。

foreach (var t in tb)
{
    var sum = 0m;
    var q = _context.SaleHeaders.Where(w=>w.cust == t.cust)
        .OrderByDescending(o=>o.saleDate)
    .Select(s => new { s.id, s.saleDate, s.invoiceValue }).AsEnumerable()
    .TakeWhile(x => { var temp = sum; sum += x.invoiceValue; return temp > t.balance; });
}

Note: Sales table is having ~75K records. 注意:Sales表有~75K记录。

More Info... 更多信息...

Customers may pay partial amount than Sale Invoice. 客户可以支付部分金额而不是销售发票。 All Payments, Invoices are posted to another table thats why balances comes from a complex query on another table. 所有付款,发票都过帐到另一个表格,这就是为什么余额来自另一个表格上的复杂查询。

Sales table has purely raw sales data. 销售表具有纯粹的原始销售数据。

Now, cust 1 balance is 1000 than the value is of last sale invoice or even last to last sale invoice's full or partial. 现在,cust 1余额是1000,而不是上次销售发票的价值,甚至是最后一次销售发票的全部或部分余额。 I need to map "balance from onwards". 我需要绘制“从头开始平衡”。

So you have two sequences: a sequence of Balances and a sequence of Sales . 所以你有两个序列:一系列的Balances和一系列的Sales

Every Balance has at least an int property CustomerId , and a BalanceValue 每个Balance至少有一个int属性CustomerId和一个BalanceValue

Every Sale has at least a nullable int property CustomerId and a DateTime property SaleDate 每个Sale至少有一个可以为空的int属性CustomerId和一个DateTime属性SaleDate

class Balance
{
     public int CustomerId {get; set;}
     public decimal BalanceValue {get; set;}
}
class Sale
{
     public int? CustomerId {get; set;}
     public DateTime SaleDate {get; set;}
} 

Apparently it is possible to have a Sale without a Customer. 显然,没有客户可以进行销售。

Now given a sequence of Balances and Sales , you want for every Balance one object, containing the CustomerId , the BalanceValue and the SaleDate that this Customer has in the sequence of Sales . 现在给出的序列BalancesSales ,你要为每一个Balance一个对象,包含了CustomerId ,该BalanceValueSaleDate这个客户有序列中Sales

Your requirement doesn't specify what you want with Balances in your sequence of Balances that have a Customer without a Sale in the sequence of Sales. 您的要求未按照销售顺序在您没有销售的客户的余额序列中指定您想要的余额。 Let's assume you want those items in your result with a null LastSaleDate . 假设您希望结果中的那些项具有null LastSaleDate

IEnumerable<Balance> balances = ...
IEnumerable<Sales> sales = ...

We are not interested in sales without a customer. 没有客户,我们对销售不感兴趣。 So let's filter them out first: 所以让我们先过滤掉它们:

var salesWithCustomers = sales.Where(sale => sale.CustomerId != null);

Now make groups of Balances with their sales, by groupjoiningon CustomerId: 现在通过groupjoiningon CustomerId制作他们的销售组的余额:

var balancesWithSales = balances.GroupJoin(salesWithCustomers, // GroupJoin balances and sales
    balance => balance.CustomerId,       // from every Balance take the CustomerId
    sale => sale.CustomerId,             // from every Sale take the CustomerId. I know it is not null,
    (balance, salesOfBalance) => new     // from every balance, with all its sales
    {                                    // make one new object  
         CustomerId = balance.CustomerId,
         Balance = balance.BalanceValue,

         LastSaleDate = salesOfBalance                // take all salesOfBalance
            .Select(sale => sale.SaleDate)            // select the saleDate
            .OrderByDescending(sale => sale.SaleDate) // order: newest dates first
            .FirstOrDefault(),                        // keep only the first element
    })

The calculation of the LastSaleDate orders all elements, and keeps only the first. LastSaleDate的计算命令所有元素,并仅保留第一个元素。

Although this solution workd, it is not efficient to order all other elements if you only need the largest one. 尽管此解决方案有效,但如果您只需要最大的元素,则订购所有其他元素效率不高。 If you are working with IEnumerables, instead of IQueryables (as would be in a database), you can optimize this, by creating a function. 如果您正在使用IEnumerables而不是IQueryables(就像在数据库中一样),您可以通过创建函数来优化它。

I implement this as an extension function. 我将其实现为扩展功能。 See extension methods demystified 看到解密的扩展方法

static DateTime? NewestDateOrDefault(this IEnumerable<DateTime> dates)
{
    IEnumerator<DateTime> enumerator = dates.GetEnumerator();
    if (!enumerator.MoveNext())
    {
        // sequence of dates is empty; there is no newest date
        return null;
    }
    else
    {   // sequence contains elements
        DateTime newestDate = enumerator.Current;
        while (enumerator.MoveNext())
        {   // there are more elements
            if (enumerator.Current > newestDate)
               newestDate = enumerator.Current);
        }
        return newestDate;
    }
}

Usage: 用法:

LastSaleDate = salesOfBalance
    .Select(sale => sale.SaleDate)
    .NewesDateOrDefault();

Now you know that instead of sorting the complete sequence you'll enumerate the sequence only once. 现在你知道,不是对整个序列进行排序,而是只列举一次序列。

Note: you can't use Enumerable.Aggregate for this, because it doesn't work with empty sequences. 注意:您不能使用Enumerable.Aggregate,因为它不适用于空序列。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM