Linq中的平均扩展方法对于默认值的处理方式

24

有人知道我如何为平均值设置默认值吗?我有一行代码像这样...

dbPlugins = (from p in dbPlugins
                select new { Plugin = p, AvgScore = p.DbVersions.Average(x => x.DbRatings.Average(y => y.Score)) })
            .OrderByDescending(x => x.AvgScore)
            .Select(x => x.Plugin).ToList();

由于我还没有评分,所以出现错误。如果我没有评分,我希望平均值默认为0。我想这应该是一个扩展方法,我可以在其中指定默认值。

3个回答

56

有一个方法:DefaultIfEmpty

我不确定你的 DbVersionsDbRatings 是什么, 也不确定哪个集合恰好有零项,但这就是思路:

var emptyCollection = new List<int>();
var average = emptyCollection.DefaultIfEmpty(0).Average();

更新:(为了增加可见性,重复下面评论中的内容)

如果你发现自己需要在类类型的集合上使用DefaultIfEmpty,请记住你可以在聚合之前改变LINQ查询项目。例如:

class Item
{
    public int Value { get; set; }
}

var list = new List<Item>();
var avg = list.Average(item => item.Value);

如果您不想或无法构建一个默认的等于0的Item,您可以先将其投影到int集合中,然后再提供一个默认值:

var avg = list.Select(item => item.Value).DefaultIfEmpty(0).Average();

我不认为这是一个好主意。假设你有一个List<A>,其中A是一个带有int属性Num的类。你需要编写像listofA.DefaultItEmpty(defaultA).Average(a => a.Num)这样的代码。你需要在这里构造一个defaultA - Cheng Chen
4
不是真的:listofA.Select(a => a.Num).DefaultIfEmpty(0).Average() - Jon
@Jon:但你现在又回到了起点,x.DbRatings 可能为空。p.DbVersions.Select(x => x.DbRatings).DefaultIfEmpty(defaultRatings),你需要指定 defaultRatings,它不仅仅是一个数字。 - Cheng Chen
2
@DannyChen:为什么 x.DbRatings 可以是 null?我肯定希望它是一个空的 IQueryable<DbRating> 而不是 null - Jon
@DannyChen:InvalidOperationException。请阅读文档:http://msdn.microsoft.com/zh-cn/library/bb354760.aspx - Jon

4

我建议您创建可重用的解决方案,而不是仅针对此问题的解决方案。

创建一个名为 AverageOrDefault 的扩展方法,类似于 FirstOrDefault。请参阅扩展方法解析

public static class MyEnumerableExtensions
{
    public static double AverageOrDefault(this IEnumerable<int> source)
    {
        // TODO: decide what to do if source equals null: exception or return default?
        if (source.Any())
            return source.Average();
        else
            return default(int);
    }
}

Enumerable.Average有9个重载,所以您需要为double、int?、decimal等创建一个AverageOrDefault。它们看起来都很相似。

使用方法:

// Get the average order total or default per customer
var averageOrderTotalPerCustomer = myDbContext.Customers
    .GroupJoin(myDbContext.Orders,
    customer => customer.Id,
    order => order.CustomerId,
    (customer, ordersOfThisCustomer) => new
    {
        Id = customer.Id,
        Name  = customer.Name,
        AverageOrder = ordersOfThisCustomer.AverageOrDefault(),
    });

0

我认为没有选择默认值的方法,但是这个查询怎么样?

dbPlugins = (from p in dbPlugins
             select new { 
                Plugin = p, AvgScore = 
                    p.DbVersions.Any(x => x.DbRatings) ?
                        p.DbVersions.Average(x => x.DbRatings.Average(y => y.Score)) : 0 })
             .OrderByDescending(x => x.AvgScore)
             .Select(x => x.Plugin).ToList();

本质上与您的相同,但我们首先询问是否有任何评级才进行平均。如果没有评级,则返回0。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接