2011-09-27 127 views
1

我有幾個列表,我需要迭代通過才能執行計算。總之,List1是道路起點和終點(ID)列表,List2是這些終點的單個速度樣本列表(每個終點有多個速度樣本)。 list1的定義是這樣的:非嵌套列表迭代性能

class RoadwaySegment 
{ 
    public int StartId {get; set;} 
    public int EndId {get; set;} 
} 

列表2的定義如下:

class IndividualSpeeds 
{ 
    public int StartHour {get; set;} 
    public int StartMin {get; set;} //either 0,15,30,or 45 
    public int Speed {get; set;} 
    public int StartId {get; set;} 
    public int EndId {get; set;} 
} 

項目list3是我的計算的結果,並將包含List1中的道路段的平均速度,每次15分鐘的時間內的一天。項目list3看起來是這樣的:

class SummaryData 
{ 
    public string SummaryHour {get; set;} 
    public string SummaryMin {get; set;} 
    public int StartId {get; set;} 
    public int EndId {get; set;} 
    public int AvgSpeed {get; set;} 
} 

目前,生成項目list3,我遍歷列表1,然後在每一天的24小時內,然後在一小時每隔15分鐘間隔。對於這些迭代中的每一個,我檢查List2中的單個速度樣本是否應包括在我的巷道段的平均速度計算中。因此,它看起來是這樣的:

var summaryList = new List<SummaryData>(); 
foreach (var segment in RoadwaySegments) 
{ 
    for(int startHour = 0; startHour < 24; startHour++) 
    { 
     for(int startMin = 0; startMin < 60; startMin+= 15) 
     { 
     int totalSpeeds = 0; 
     int numSamples = 0; 
     int avgSpeed = 0; 

     foreach(var speedSample in IndividualSpeeds) 
     { 
      if((segment.StartId == speedSample.StartId)&&(segment.EndId == speedSample.EndId)&&(speedSample.StartHour == startHour)&&(speedSample.StartMin == startMin)) 
      { 
       if(speedSample.Speed > 0) 
       { 
        totalSpeeds += speedSample.Speed; 
        numSamples += 1; 
       } 
      } 
     } 
     SummaryData summaryItem = new SummaryData {SummaryHour = startHour, SummaryMin = startMin, StartId = segment.StartId, EndId = segment.EndId, AvgSpeed = totalSpeeds/numSamples; 
     summaryList.Add(summaryItem); 
     } 
    } 
} 

這段代碼的問題是,列表1可能有一百個道路段,但列表2可容納一百萬以上的高速採樣記錄,以便列表的子迭代是很長的時間耗時。有沒有一種方法可以使用GroupBy/LINQ來提高此代碼的性能和可讀性?注意條件包括在平均的速度 - 它必須是大於0

回答

3

這是未經測試,但我認爲這會工作:

from segment in RoadwaySegments 
join sample in IndividualSpeeds on new { segment.StartId, segment.EndId } equals new { sample.StartId, sample.EndId } into segmentSamples 
from startHour in Enumerable.Range(0, 24) 
from startMin in new[] { 0, 15, 30, 45 } 
let startMinSamples = segmentSamples 
    .Where(sample => sample.Speed > 0) 
    .Where(sample => sample.StartHour == startHour) 
    .Where(sample => sample.StartMin == startMin) 
    .Select(sample => sample.Speed) 
    .ToList() 
select new SummaryItem 
{ 
    StartId = segment.StartId, 
    EndId = segment.EndId, 
    SummaryHour = startHour, 
    SummaryMin = minute, 
    AvgSpeed = startMinSamples.Count <= 2 ? 0 : startMinSamples.Average() 
}; 

的主要思想是迭代段和樣本列表一次,爲每個分段生成一組樣本。然後,爲每個組生成小時和分鐘以及每個組合的摘要項目。最後,您計算小時/分鐘組合中所有非零樣本的平均速度。

這不是很理想,因爲您仍然會對段的樣本進行24 * 4次迭代,但它比迭代整個樣本列表要好得多。這應該讓你走上正確的道路,並希望你可以進一步優化最後一點。

+0

不錯,但它不是C#了。我覺得很難閱讀 - 是我嗎?我缺乏LINQ經驗和培訓嗎?或者在這樣長而複雜的查詢中是否存在真正的概念問題? –

+1

@Amittai Shapira:這是合法的C# - 我不確定你的意思。我發現這比OP所指定的等價循環更容易閱讀;有更少的變量,可變列表,if語句等等。它還說明它正在做什麼(加入),而不必解密一堆邏輯來找出它的含義。這就是爲什麼我喜歡LINQ:強調*什麼*和低估*如何*。 –

+0

感謝您的解釋,我學到了很多東西。 –

2

如果您使用.net 4,我會建議將其與Parallel Linq並行。這與RoadwaySegments相比令人尷尬。其次,我不建議迭代該列表一次,而是用StartId,EndId,StartHour和EndHour的Composite Key創建List<IndividualSpeeds>字典。在這個詞典中進行查找將是更多與重新對每個RoadwaySegment進行比較。

0

以下是克里斯和布賴恩的答案:
既然你說可能有數百萬的速度樣本記錄,你只是不想迭代它們超過最小可能 - 即你應該分組根據他們的開始時間和分鐘,使用GroupByJoin運營商)。您可以迭代每個組,然後將每個示例記錄添加到RoadwaySegments的某個字典中(類似Dictionary<RoadwaySegment, IEnumerable<IndividalSpeeds>>),並從此字典中創建摘要項目。