如何根据总计

时间:2015-08-09 17:03:33

标签: c# linq linq-to-objects

我有一个要求,我需要分组并选择运行量超过阈值10的事务。一旦超过阈值,运行计数将被重置。

这是我想要做的一个例子......

以下是一些交易:

Id | Amount
1  | 5.50
2  | 4.10
3  | 1.20
4  | 1.05
5  | 3.25
6  | 1.25
7  | 5.15
8  | 8.15
9  | 5.15

我想要达到的结果是:

第1组:

Id | Amount
1  | 5.50
2  | 4.10    
3  | 1.20

第2组:

4  | 1.05
5  | 3.25    
6  | 1.25   
7  | 5.15  

第3组:

8  | 8.15
9  | 5.15

我想出了一个使用for循环和yield的解决方案。您可以在https://dotnetfiddle.net/rcSJO4以及下方进行查看。

我只是想知道是否有一个更优雅的解决方案,如果有一个聪明且更易读的方法可以使用Linq实现。

我的解决方案:

using System;
using System.Linq;
using System.Collections.Generic;

public class Transaction{
    public int Id { get; set;}
    public decimal Amount { get; set;}        
}


public class Program
{
    public static void Main()
    {

        var transactions = new Transaction [] { 
            new Transaction {Id = 1, Amount = 5.50m},
            new Transaction {Id = 2, Amount = 4.10m},
            new Transaction {Id = 3, Amount = 1.20m},
            new Transaction {Id = 4, Amount = 1.05m},
            new Transaction {Id = 5, Amount = 3.25m},
            new Transaction {Id = 6, Amount = 1.25m},
            new Transaction {Id = 7, Amount = 5.15m},
            new Transaction {Id = 8, Amount = 8.15m},
            new Transaction {Id = 9, Amount = 5.15m},
        };

        var grouped = ApplyGrouping(transactions);

        foreach(var g in grouped)
        {
            Console.WriteLine("Total:" + g.Item1);

            foreach(var t in g.Item2){
                Console.WriteLine(" " +t.Amount);
            }
            Console.WriteLine("---------");
        }

    }

    private static IEnumerable<Tuple<decimal, IEnumerable<Transaction>>> ApplyGrouping(IEnumerable<Transaction> transactions){

        decimal runningTotal = 0m;
        decimal threshold = 10m;

        var grouped = new List<Transaction>();

        foreach(var t in transactions){

            grouped.Add(t);
            runningTotal += t.Amount;

            if (runningTotal <= threshold) continue;

            yield return new Tuple<decimal, IEnumerable<Transaction>>(runningTotal, grouped);

            grouped.Clear();
            runningTotal = 0;
        }

    }
}

3 个答案:

答案 0 :(得分:0)

我不知道你是否会找到一个完全令人满意的解决方案。没有内置的Linq运算符(或运算符的组合)将以美学上令人愉悦的方式执行此操作。您的方法 - 将复杂性重构为扩展方法 - 是最好的方法。没有什么不妥。无论如何,这就是Linq的全部;只是一个隐藏杂乱位的抽象。但是,如果您发现自己经常使用此模式,我会说您可以将代码重构为更一般的内容。你现在拥有的只是在这个非常具体的情况下工作。如果您这样做:

public static class EnumerableExtensions
{
    public static IEnumerable<TResult> GroupUntil<TSource, TAccumulation, TResult>(
        this IEnumerable<TSource> source,
        Func<TAccumulation> seedFactory,
        Func<TAccumulation, TSource, TAccumulation> accumulator,
        Func<TAccumulation, bool> predicate,
        Func<TAccumulation, IEnumerable<TSource>, TResult> selector)
    {
        TAccumulation accumulation = seedFactory();
        List<TSource> result = new List<TSource>();
        using(IEnumerator<TSource> enumerator = source.GetEnumerator())
        {
            while(enumerator.MoveNext())
            {
                result.Add(enumerator.Current);
                accumulation = accumulator(accumulation, enumerator.Current);
                if(predicate(accumulation))
                {
                    yield return selector(accumulation, result);
                    accumulation = seedFactory();
                    result = new List<TSource>();
                }
            }

            if(result.Count > 0)
            {
                yield return selector(accumulation, result);
            }
        }
    }
}

然后,对于您的情况,您可以这样称呼它:

var grouped = 
    transactions
    .GroupUntil(
        () => 0.0m,
        (accumulation, transaction) => accumulation + transaction.Amount,
        (accumulation) => accumulation > 10.0m,
        (accumulation, group) => new { Total = accumulation, Transactions = group})

但它足够通用,可以在其他地方使用。

答案 1 :(得分:-1)

我会坚持你的解决方案。即使你可以使用LINQ进行破解,它也很可能无法读取。

你应该改变的唯一方法是一遍又一遍地使用相同的,并在返回后清除它 - 因为al.add(""); al.add(""); al.add(""); al.add(""); al.add(""); System.out.println(al); // Prints [, , , , ] 是一个参考类型,你也清除了已经返回的项目。

在前一个上创建新的List<T>List<T>个实例。

List<T>

答案 2 :(得分:-1)

这是一种LINQ方式

    var runningTotal = 0.0m;

    List<List<Transaction>> list = new List<List<Transaction>>();
    List<Transaction> currentList = new List<Transaction>();

    transactions.Select(x => {
        runningTotal += x.Amount;
        currentList.Add(x);
        if (runningTotal >= 10)
        {
            Console.WriteLine("Group 1 Total {0}", runningTotal);
            runningTotal = 0;
            list.Add(currentList);
            currentList = new List<Transaction>();
        }
        return runningTotal;
        }).ToList();