2012-02-29 5 views
1

非常に単純な分布図を作成しようとしていますが、テストのスコアの割合を対応する10の範囲に表示したいとします。LINQグルーピング:forループなしでこれを行うクリーンな方法がありますか

私はMath.Round(d.Percentage/10-0.5)、0)* 10のグループ化を考えていましたが、これは10の値を与えるはずです....しかし、私はおそらく範囲が不足しており、カウントがゼロであってもすべての範囲が表示される必要があることを考えれば、これを行う方法です。私はまた範囲配列の外部結合を行うことについて考えましたが、私はLinqにとってかなり新しいので、時間のために以下のコードを選択しました。私はしかし、より良い方法が何であるかを知りたいです。

メモ:経験レベルの異なる大規模なチームで作業する傾向があるため、平均的な開発者には非常に読みやすいままでない限り、超コンパクトなコードについては夢中ではありません。

提案がありますか?

public IEnumerable<TestDistribution> GetDistribution() 
    { 
     var distribution = new List<TestDistribution>(); 
     var ranges = new int[] { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110 }; 
     var labels = new string[] { "0%'s", "10%'s", "20%'s", "30%'s", "40%'s", "50%'s", "60%'s", "70%'s", "80%'s", "90%'s", "100%'s", ">110% "}; 

     for (var n = 0; n < ranges.Count(); n++) 
     { 
      var count = 0; 
      var min = ranges[n]; 
      var max = (n == ranges.Count() - 1) ? decimal.MaxValue : ranges[n+1]; 

      count = (from d in Results 
        where d.Percentage>= min 
        && d.Percentage<max 
        select d) 
        .Count(); 

      distribution.Add(new TestDistribution() { Label = labels[n], Frequency = count }); 
     } 

     return distribution; 
    } 
+0

わずかなカーブボールを動作します。いくつかのテストの後、範囲を<25,25-50,50-60,60-70,70-85,85-100(レターグレードを表す)に調整し、 "U"、 "F"、および " 「D」、「C」、「B」、「A」。これらの範囲は動的に設定され、ハードコードされていないため、単純なインクリメンタを使用してグループを定義する方法はありません。 – user1240087

+0

範囲のクラス/構造体はMin、Max、Labelの各プロパティで役立ちます。 –

答えて

1
// ranges and labels in a list of pairs of them 

var rangesWithLabels = ranges.Zip(labels, (r,l) => new {Range = r, Label = l}); 

// create a list of intervals (ie. 0-10, 10-20, .. 110 - max value 
var rangeMinMax = ranges.Zip(ranges.Skip(1), (min, max) => new {Min = min, Max = max}) 
         .Union(new[] {new {Min = ranges.Last(), Max = Int32.MaxValue}}); 

//the grouping is made by the lower bound of the interval found for some Percentage  
var resultsDistribution = from c in Results        
      group c by 
       rangeMinMax.FirstOrDefault(r=> r.Min <= c.Percentage && c.Percentage < r.Max).Min into g 
      select new {Percentage = g.Key, Frequency = g.Count() };        
// left join betweem the labels and the results with frequencies  
var distributionWithLabels = 
     from l in rangesWithLabels 
     join r in resultsDistribution on l.Range equals r.Percentage 
       into rd 
       from r in rd.DefaultIfEmpty()     
     select new TestDistribution{ 
      Label = l.Label, 
      Frequency = r != null ? r.Frequency : 0 
     }; 
distribution = distributionWithLabels.ToList(); 




別の解決策の範囲およびラベルは別の方法

var ranges = Enumerable.Range(0, 10) 
       .Select(c=> new { 
         Min = c * 10, 
         Max = (c +1)* 10, 
         Label = (c * 10) + "%'s"}) 
       .Union(new[] { new { 
          Min = 100, 
         Max = Int32.MaxValue, 
         Label = ">110% " 
       }}); 
var resultsDistribution = from c in Results        
          group c by ranges.FirstOrDefault(r=> r.Min <= c.Percentage && c.Percentage < r.Max).Min 
            into g 
          select new {Percentage = g.Key, Frequency = g.Count() }; 

var distributionWithLabels = 
     from l in ranges 
     join r in resultsDistribution on l.Min equals r.Percentage 
       into rd 
       from r in rd.DefaultIfEmpty()     
     select new TestDistribution{ 
      Label = l.Label, 
      Frequency = r != null ? r.Frequency : 0 
     }; 
0

で作成することができる場合、これは

public IEnumerable<TestDistribution> GetDistribution() 
{ 
    var range = 12; 

    return Enumerable.Range(0, range).Select(
     n => new TestDistribution 
       { 
        Label = string.Format("{1}{0}%'s", n*10, n==range-1 ? ">" : ""), 
        Frequency = 
         Results.Count(
          d => 
          d.Percentage >= n*10 
          && d.Percentage < ((n == range - 1) ? decimal.MaxValue : (n+1)*10)) 
       }); 
} 
+0

私はこれをまだテストしていませんが、私はそれが好きです。私は範囲の単純な配列を作成し、上で行ったように選択を行うことができると思います。良いアイデア:) – user1240087

+0

それはあなたのために働いたか? – Phil

関連する問題