[ACCEPTED]-Average extension method in Linq for default value-average

Accepted answer
Score: 54

There is: DefaultIfEmpty.

I 'm not sure about what your 10 DbVersions and DbRatings are and which collection exactly has 9 zero items, but this is the idea:

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

Update: (repeating 8 what's said in the comments below to increase 7 visibility)

If you find yourself needing 6 to use DefaultIfEmpty on a collection of class type, remember 5 that you can change the LINQ query to project 4 before aggregating. For example:

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

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

If you don't want 3 to/can not construct a default Item with Value equal 2 to 0, you can project to a collection of 1 ints first and then supply a default:

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

My advice would to create a reusable solution instead of a solution for this problem only.

Make an extension method 5 AverageOrDefault, similar to FirstOrDefault. See 4 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);
    }
}

There are 9 overloads of Enumerable.Average, so 3 you'll need to create an AverageOrDefault 2 for double, int?, decimal, etc. They all 1 look similar.

Usage:

// 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(),
    });
Score: 0

I don't think there's a way to select default, but 4 how about this query

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();

Essentially the same 3 as yours, but we first ask if there are 2 any ratings before averaging them. If not, we 1 return 0.

More Related questions