根据优先级可枚举的分区

时间:2018-03-27 15:32:47

标签: c# linq

我收到了IEnumerable<TransactionLineDto>TransactionLineDto可以代表适用于Article的{​​{1}}或Discount

订单很重要:Article仅适用于前面的文章,直到找到另一个折扣。

根本没有折扣(只有Discount代表TransactionLineDto)。

一个Article可以有很多折扣。

文章上的折扣不能超过1个。

示例:

  1. IEnumerable<TransactionLineDto>表示有3篇文章没有任何折扣。
  2. Article1 -> Article2 -> Article3表示Article1 -> Article2 -> Discount1适用于Discount1Article1
  3. Article2表示Article1 -> Article2 -> Discount1 -> Article3 -> Discount2 -> Article4适用于Discount1Article1Article2 Discount2Article3没有折扣

    现在我需要“分区”这些序列,以便能够知道哪个折扣适用于哪篇文章(必须放弃没有折扣的文章)。

    第三个示例的预期输出应该是这样的:Article4

    如何实现可以对此行为进行建模的数据结构/类? 我想到了类似的东西(注意我无法实现的(Discount1 -> Article1, Article2), (Discount2 -> Article3)):

    ???

    这是一个好方法还是过度杀伤? (我的意思是,只有使用预先存在的LINQ方法才能做到这一点吗?)

2 个答案:

答案 0 :(得分:1)

使用基于APL扫描操作符的(我最喜欢的)自定义扩展方法(它就像Aggregate只返回中间结果),您可以扫描反转的IEnumerable并收集折扣,然后按折扣分组。

自定义扫描扩展方法:

public static IEnumerable<TResult> Scan<T, TResult>(this IEnumerable<T> src, TResult seed, Func<TResult, T, TResult> combine) {
    foreach (var s in src) {
        seed = combine(seed, s);
        yield return seed;
    }
}

然后您可以Scan沿IEnumerable收集每篇文章的折扣,然后按折扣分组:

var discountPartitions = src.Reverse()
                            .Scan((discount: (TransactionLineDto)null, article: (TransactionLineDto)null),
                                  (daTuple, dto) => dto.IsDiscount ? (dto, (TransactionLineDto)null)
                                                                   : (daTuple.discount != null ? (daTuple.discount, dto) : daTuple)
                                 )
                            .Where(daTuple => daTuple.discount != null && daTuple.article != null)
                            .GroupBy(daTuple => daTuple.discount, daTuple => daTuple.article);

答案 1 :(得分:1)

public static IEnumerable<List<T>> ToClusters(
  this IEnumerable<T> source, Func<T, bool> endOfClusterCriteria)
{
  var result = List<T>();
  foreach(T item in source)
  {
    result.Add(item);
    if (endOfClusterCriteria(item))
    {
      yield return result;
      result = new List<T>(); 
    }
  }
}

后:

var lookup = transactionLines
  .ToClusters(line => line.IsDiscount())
  .Where(cluster => 2 <= cluster.Count) //discard the discounts with no articles
  .SelectMany(
    cluster => cluster.Take(cluster.Count - 1),
    (cluster, article) => new { Discount = cluster.Last(), Article = article })
  .ToLookup(
    pair => pair.Discount,
    pair => pair.Article
  ).ToList();

我发现.SelectMany调用难以阅读,所以这与查询理解相同:

var lookup = (
  from cluster in transactionLines.ToClusters(line => line.IsDiscount)
  where 2 <= cluster.Count
  from article in cluster.Take(cluster.Count - 1)
  select new { Discount = cluster.Last(), Article = article }
  ).ToLookup(pair => pair.Discount, pair => pair.Article);