LINQ分组:没有for循环,有没有更简洁的方法来做到这一点

时间:2012-02-29 11:37:41

标签: linq

我正在尝试创建一个非常简单的分布图,我想在相应的10个范围内显示测试分数百分比。

我想过只是在Math.Round上进行分组((d.Percentage / 10-0.5),0)* 10这应该给我10的价值....但我不确定最好的方法考虑到我可能会丢失范围并且即使计数为零也需要出现所有范围。我还想过在range数组上做一个外连接,但因为我对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;
    }

2 个答案:

答案 0 :(得分: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
        };

答案 1 :(得分: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))
                });
}