2015-04-22 57 views
3

我有一個linq查詢結果,如圖所示。在最後一個查詢中(未顯示),我按LeaveType按年分組。不過,我想計算多年來每種類型的leaveCarriedOver的總運行總數。也就是說,在2010年生病LeaveCarriedOver成爲2011病假「開放」的平衡,加上一個2011年如何使用linq計算運行總數

enter image description here

我已經做到了這看起來像出結果列表上的其他查詢:

  var leaveDetails1 = (from l in leaveDetails 
      select new 
      { 
       l.Year, 
       l.LeaveType, 
       l.LeaveTaken, 
       l.LeaveAllocation, 
       l.LeaveCarriedOver, 
       RunningTotal = leaveDetails.Where(x => x.LeaveType == l.LeaveType).Sum(x => x.LeaveCarriedOver) 
      }); 

其中,leaveDetails是圖像的結果。

生成的RunningTotal不按預期進行累積。我怎樣才能達到我的最初目標。打開任何想法 - 我的最後一個選擇是在前端使用JavaScript。在此先感謝

+1

也許你可以使用LINQ的Aggregate方法:https://msdn.microsoft.com/en-us/library/system.linq.enumerable.aggregate.aspx。這相當於函數式編程的一個摺疊。 –

+0

我會給你一些關於如何解決這個問題的提示,你可以通過得到Year('int')和LeaveType(我認爲它是'string')的'distinct''List >'來實現。爲了得到這個,一個簡單的'Distinct <>'後跟一個'Select <>'應該給你。那麼這個清單就是你實際積累的總數。現在使用這個新列表,你需要做一個'Select <>'並且返回一個新的對象,所以可以說'Tuple '其中前2個參數與另一個列表相同,但是最後一個int需要使用'Where'>'過濾year/leavetype的細節列表,然後返回'Sum <>'。 – Franck

+0

@Franck你的評論對我來說是有意義的,但我只是沒有正確使用我的linq查詢。你可以請實施答覆嗎?謝謝 – mboko

回答

0

簡單的實現是首先獲得可能的總計列表,然後從每個類別的詳細信息中獲得總和。

得到不同的清單YearLeaveType是一組,並選擇每組的第一個。我們返回List<Tuple<int, string>>其中Int是一年string是LeaveType

var distinctList = leaveDetails1.GroupBy(data => new Tuple<int, string>(data.Year, data.LeaveType)).Select(data => data.FirstOrDefault()).ToList(); 

然後我們要對各元素的總讓你想有一個選擇列表返回ID(YearLeaveType)加總所以額外的價值Tuple<int, string, int>

var totals = distinctList.Select(data => new Tuple<int, string, int>(data.Year, data.LeaveType, leaveDetails1.Where(detail => detail.Year == data.Year && detail.LeaveType == data.LeaveType).Sum(detail => detail.LeaveCarriedOver))).ToList(); 

讀線路上面可以看到它需要我們要列出不同的總計,創建一個新的對象,存儲YearLeaveType參考,然後設置最後Int與該過濾的細節Sum<>YearLeaveType

0

如果我完全理解你正在嘗試做什麼,那麼我不認爲我會完全依賴於內置的LINQ操作符。我認爲(強調認爲)內置LINQ操作符的任何組合都將在O(n^2)運行時解決這個問題。

如果我打算在LINQ實現這個話,我會給IEnumerable的擴展方法是類似反應擴展的Scan功能(或找到一個圖書館,在那裏,已經實現了它):

public static class EnumerableExtensions 
{ 
    public static IEnumerable<TAccumulate> Scan<TSource, TAccumulate>(
     this IEnumerable<TSource> source, 
     TAccumulate seed, 
     Func<TAccumulate, TSource, TAccumulate> accumulator) 
    { 
     // Validation omitted for clarity. 
     foreach(TSource value in source) 
     { 
      seed = accumulator.Invoke(seed, value); 
      yield return seed; 
     } 
    } 
} 

那麼這應該做到這一點各地爲O(n log n)的(因爲操作的順序):

leaveDetails 
.OrderBy(x => x.LeaveType) 
.ThenBy(x => x.Year) 
.Scan(new { 
     Year = 0, 
     LeaveType = "Seed", 
     LeaveTaken = 0, 
     LeaveAllocation = 0.0, 
     LeaveCarriedOver = 0.0, 
     RunningTotal = 0.0 
    }, 
    (acc, x) => new { 
     x.Year, 
     x.LeaveType, 
     x.LeaveTaken, 
     x.LeaveAllocation, 
     x.LeaveCarriedOver, 
     RunningTotal = x.LeaveCarriedOver + (acc.LeaveType != x.LeaveType ? 0 : acc.RunningTotal) 
    }); 

你不說,但我相信數據是從數據庫中來;如果是這種情況,那麼你可以得到leaveDetails已經排序,並跳過這裏排序。這會讓你下降到O(n)。

如果你不想創建一個擴展方法(或去找一個),那麼這將實現相同的事情(只是以一種更醜陋的方式)。

var temp = new 
    { 
     Year = 0, 
     LeaveType = "Who Cares", 
     LeaveTaken = 3, 
     LeaveAllocation = 0.0, 
     LeaveCarriedOver = 0.0, 
     RunningTotal = 0.0 
    }; 
var runningTotals = (new[] { temp }).ToList(); 
runningTotals.RemoveAt(0); 

foreach(var l in leaveDetails.OrderBy(x => x.LeaveType).ThenBy(x => x.Year)) 
{ 
    var s = runningTotals.LastOrDefault(); 
    runningTotals.Add(new 
     { 
      l.Year, 
      l.LeaveType, 
      l.LeaveTaken, 
      l.LeaveAllocation, 
      l.LeaveCarriedOver, 
      RunningTotal = l.LeaveCarriedOver + (s == null || s.LeaveType != l.LeaveType ? 0 : s.RunningTotal) 
     }); 
} 

這也應該是爲O(n log n)的或爲O(n),如果你能預先整理leaveDetails

0

如果我沒有理解這個問題,你想要的東西,像

decimal RunningTotal = 0; 

var results = leaveDetails 
    .GroupBy(r=>r.LeaveType) 
    .Select(r=> new 
    { 
     Dummy = RunningTotal = 0 , 
     results = r.OrderBy(o=>o.Year) 
     .Select(l => new 
      { 
       l.Year, 
       l.LeaveType , 
       l.LeaveAllocation, 
       l.LeaveCarriedOver, 
       RunningTotal = (RunningTotal = RunningTotal + l.LeaveCarriedOver) 
      }) 
    }) 
    .SelectMany(a=>a.results).ToList(); 

這基本上是使用Select<TSource, TResult>超載來計算運行平衡,但首先由LeaveType分組,這樣我們就可以重新設置RunningTotal爲每LeaveType,然後最終未分組。