2015-08-09 4 views
3

У меня есть требование, когда мне нужно группировать и выбирать транзакции, в которых количество запусков превышает пороговое значение 10. После превышения порогового значения счетчик будет сброшен ,Как группировать и или выбирать в Linq на основе общего количества

Вот пример того, что я пытаюсь сделать ...

Ниже приведены некоторые сделки:

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 

Я придумал решение, которое использует для цикла и выхода. Вы можете увидеть его на 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; 
     } 

    } 
} 

ответ

-1

Я бы придерживаться своего решения. Даже если вы можете взломать его с помощью LINQ, скорее всего, он не будет доступен для чтения.

Единственное, что вы должны изменить, - это использовать один и тот же List<T> снова и снова и очистить его после возвращения - потому что List<T> - это ссылочный тип, который вы уже очищаете уже возвращенные элементы.

List<T> instad of call Clear предыдущий.

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 = new List<Transaction>(); 
     runningTotal = 0; 
    } 
} 
+0

... но он ищет LINQ –

-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(); 
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}) 

Но это достаточно универсален, что его можно использовать в других местах.

 Смежные вопросы

  • Нет связанных вопросов^_^