【问题标题】:how to use LINQ TakeWhile and Join to a list如何使用 LINQ TakeWhile 和加入列表
【发布时间】:2019-05-20 23:42:27
【问题描述】:

我想将最早的信用发票日期映射到销售表中的列表。我有一个以下方式的列表。

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

这些余额是几张发票的累积,有时可能是一张发票。

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...
}

客户 1 的样本数据

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

如果我现在取,报告应该如下。

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

有没有简单的方法可以通过 LINQ 实现这一点。

我尝试使用 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; });
}

注意:Sales 表有大约 75K 记录。

更多信息...

客户可以支付部分金额而不是销售发票。所有付款、发票都发布到另一个表,这就是为什么余额来自另一个表上的复杂查询。

Sales 表包含纯粹的原始销售数据。

现在,cust 1 的余额是 1000,而不是最后一张销售发票的价值,甚至最后一张销售发票的全部或部分价值。我需要映射“从头开始的平衡”。

【问题讨论】:

  • 客户 1 的 1000 是从哪里获得的?你有两个发票值 500 和 750,1K 是从哪里来的?
  • Cust 1 balance 来自其他表上的其他一些复杂查询。当客户支付部分付款时,可能会发生。涉及价值和支付价值可能不同。

标签: c# linq entity-framework-core-2.1


【解决方案1】:

所以你有两个序列:Balances 序列和Sales 序列。

每个Balance 至少有一个int 属性CustomerId 和一个BalanceValue

每个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;}
} 

显然,没有客户也可以进行销售。

现在给定BalancesSales 的序列,您希望为每个Balance 一个对象,包含CustomerIdBalanceValueSaleDate,此客户在@ 序列中拥有987654336@.

您的要求没有指定您想要的余额序列中的余额,这些余额在销售序列中有一个客户没有销售。假设您希望结果中的这些项目带有 null LastSaleDate

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

我们对没有客户的销售不感兴趣。所以让我们先过滤掉它们:

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

现在通过 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
    })

LastSaleDate 的计算对所有元素进行排序,只保留第一个。

尽管此解决方案有效,但如果您只需要最大的元素,则对所有其他元素进行排序并不高效。如果您使用的是 IEnumerables,而不是 IQueryables(就像在数据库中一样),您可以通过创建一个函数来优化它。

我将其作为扩展函数来实现。见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;
    }
}

用法:

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

现在您知道了,您只需枚举序列一次,而不是对完整序列进行排序。

注意:您不能为此使用 Enumerable.Aggregate,因为它不适用于空序列。

【讨论】:

  • 我希望 SaleDate 为“余额”,而不是 LastSaleDate。前任。余额可能超过 LastSaleDate 的发票价值。
  • 好吧,也许您应该编辑您的问题并编写您想要的适当要求,而不是给我们不完整的示例,我们必须尝试从中提取这些要求
  • 样本数据和预期输出有问题。
  • 这个例子没有说任何关于“从以后开始的余额”(无论可能是什么),也没有说 LastSaleDate 的发票价值对您请求的结果有任何影响
  • 问题已编辑。如果需要更多信息,请告诉我。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多