Linq 中默认值的平均扩展方法
Posted
技术标签:
【中文标题】Linq 中默认值的平均扩展方法【英文标题】:Average extension method in Linq for default value 【发布时间】:2011-07-24 22:31:29 【问题描述】:有人知道如何设置平均值的默认值吗?我有这样的台词……
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。我在想这应该是一种扩展方法,我可以在其中指定默认值应该是什么。
【问题讨论】:
【参考方案1】:我的建议是创建一个可重复使用的解决方案,而不仅仅是针对这个问题的解决方案。
制作一个扩展方法AverageOrDefault,类似于FirstOrDefault。见extension methods demystified
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(),
);
【讨论】:
【参考方案2】:有:DefaultIfEmpty
。
我不确定您的 DbVersions
和 DbRatings
是什么,以及哪个集合恰好有零项,但这就是想法:
var emptyCollection = new List<int>();
var average = emptyCollection.DefaultIfEmpty(0).Average();
更新:(重复以下 cmets 中所说的以增加可见性)
如果您发现自己需要在类类型的集合上使用DefaultIfEmpty
,请记住您可以将 LINQ 查询更改为 before 聚合。例如:
class Item
public int Value get; set;
var list = new List<Item>();
var avg = list.Average(item => item.Value);
如果您不想/不能构造一个默认的Item
且Value
等于0,您可以先投影到int
s 的集合,然后然后提供一个默认:
var avg = list.Select(item => item.Value).DefaultIfEmpty(0).Average();
【讨论】:
我认为这不是一个好主意。假设你有一个List<A>
,而A
是一个具有int
属性Num
的类。您需要编写类似listofA.DefaultItEmpty(defaultA).Average(a => a.Num)
的代码。你需要在这里构造一个defaultA
。
@DannyChen:不正确:listofA.Select(a => a.Num).DefaultIfEmpty(0).Average()
.
@Jon:但你现在又回到了起点,x.DbRatings
在这里可以为空。 p.DbVersions.Select(x => x.DbRatings).DefaultIfEmpty(defaultRatings)
,需要指定defaultRatings
,不是简单的数字。
@DannyChen: 为什么x.DbRatings
可以成为null
?我当然希望它是一个空的 IQueryable<DbRating>
而不是 null
。
@DannyChen:InvalidOperationException
。请阅读文档:msdn.microsoft.com/en-us/library/bb354760.aspx【参考方案3】:
我认为没有办法选择默认值,但是这个查询怎么样
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。
【讨论】:
以上是关于Linq 中默认值的平均扩展方法的主要内容,如果未能解决你的问题,请参考以下文章
Linq 中 Enumerable.Zip 扩展方法有啥用?