使用LINQ从类属性计算百分比的更好方法

时间:2018-12-12 21:04:35

标签: c# linq

我们有以下LINQ定义:

myList.Select(s=> new DtoTest()
{
    TotalSamples = myList.Count(c=> c.UserId == s.UserId),
    EvaluatedSamples = myList.Count(c=> c.UserId == s.UserId && c.Status == Status.OK)
    PercentageRealized = (myList.Count(c=> c.UserId == s.UserId) / myList.Count(c=> c.UserId == s.UserId && c.Status == Status.OK)) * 100
});

有没有一种方法可以分配属性值“ PercentageRealized”而不使用以前在“ TotalSamples”和“ EvaluatedSamples”中使用的相同功能?

类似的东西:

myList.Select(s=> new DtoTest()
{
    TotalSamples = myList.Count(c=> c.UserId == s.UserId),
    EvaluatedSamples = myList.Count(c=> c.UserId == s.UserId && c.Status == Status.OK)
    PercentageRealized = (TotalSamples / EvaluatedSamples) * 100 //<-!Not possible!
});

还有其他提示吗?

5 个答案:

答案 0 :(得分:3)

更改函数委托以使用已经计算的值

myList.Select(s => {
    var result = new DtoTest() {
        TotalSamples = myList.Count(c => c.UserId == s.UserId),
        EvaluatedSamples = myList.Count(c => c.UserId == s.UserId && c.Status == Status.OK)
    };
    result.PercentageRealized = (result.TotalSamples / result.EvaluatedSamples) * 100;
    return result;
});

答案 1 :(得分:3)

如果您使用的是匿名类型,这会更加复杂,但是由于DtoTest是一个类,因此您始终可以将数学运算移到该属性中。

public class DtoTest
{
    public float PercentageRealized
    {
        get { return (TotalSamples / EvaluatedSamples) * 100; }
    }
}

答案 2 :(得分:2)

如果PercentageRealized就是这么简单的计算,为什么不在类的属性中进行计算,就像在DTOTest类中这样:

public float PercentageRealized => (TotalSamples / EvaluatedSamples) * 100;

答案 3 :(得分:1)

首先投影到元组,然后投影到您的自定义对象:

 myList.Select(s => (tSample: myList.Count(c=> c.UserId == s.UserId), 
       eSample : myList.Count(c=> c.UserId == s.UserId && c.Status == Status.OK)))
      .Select(x => new DtoTest
      {
             TotalSamples = x.tSample,
             EvaluatedSamples = x.eSample,
             PercentageRealized = (x.tSample / x.eSample) * 100 
     });

或使用匿名类型:

myList.Select(s => new 
      {
        tSample = myList.Count(c=> c.UserId == s.UserId),
        eSample = myList.Count(c=> c.UserId == s.UserId && c.Status == Status.OK)
      })
      .Select(x => new DtoTest
      {
            TotalSamples = x.tSample,
            EvaluatedSamples = x.eSample,
            PercentageRealized = (x.tSample / x.eSample) * 100 
     });

答案 4 :(得分:1)

您正在做的事情对我来说极度可疑-您正在遍历源数据以一次又一次地(每次UserId)重新计算同一件事,而这似乎是您的事情应该要的是每个UserId计算一次,就像这样:

var ans2 = myList.GroupBy(s => s.UserId)
                .Select(sg => {
                    var ts = sg.Count();
                    var es = sg.Count(c => c.Status == Status.OK);
                    return new DtoTest { UserId = sg.Key, TotalSamples = ts, EvaluatedSamples = es, PercentageRealized = (int)(100.0 * ts / es) };
                });

此外,除非您先转换为double,否则您的百分比计算将使用C#整数除法并且接近正确。您可以在数学运算后回退到int

如果您确实打算返回多个结果,并且想要提高效率(并且由于我喜欢扩展方法),则可以创建一个扩展方法来为一次通过计算链接谓词:

public static class IEnumerableExt {
    public static (int Cond1Count, int Cond2Count) Count2Chained<T>(this IEnumerable<T> src, Func<T, bool> cond1, Func<T, bool> cond2) {
        int cond1Count = 0;
        int cond2Count = 0;
        foreach (var s in src) {
            if (cond1(s)) {
                ++cond1Count;
                if (cond2(s))
                    ++cond2Count;
            }
        }
        return (cond1Count, cond2Count);
    }
}

现在您可以一遍计算子值并计算第三个值:

var ans3 = myList.Select(s => {
            var (ts, es) = myList.Count2Chained(c => c.UserId == s.UserId, c => c.Status == Status.OK);
            return new DtoTest { UserId = s.UserId, TotalSamples = ts, EvaluatedSamples = es, PercentageRealized = (int)(100.0 * ts / es) };
        });

当然,根据myList的大小,您最好还是只计算一次答案,然后对最终答案重复一次:

var ansd = myList.GroupBy(s => s.UserId)
                 .Select(sg => {
                     var ts = sg.Count();
                     var es = sg.Count(c => c.Status == Status.OK);
                     return new { sg.Key, ts, es };
                 })
                 .ToDictionary(ste => ste.Key, ste => new DtoTest {
                    UserId = ste.Key,
                    TotalSamples = ste.ts,
                    EvaluatedSamples = ste.es,
                    PercentageRealized = (int)(100.0 * ste.ts / ste.es) });
var ans4 = myList.Select(s => ansd[s.UserId]);