Kuzco
Kuzco

Reputation: 315

Object initializers in a LINQ query - is it possible to reuse calculated data?

I'm using a linq query which looks (after some simplification) something like the following:

List<UserExams> listUserExams = GetUserExams();

var examData = 
from userExam in listUserExams
group by userExam.ExamID into groupExams
select new ExamData()
{
    ExamID = groupExams.Key,
    AverageGrade = groupExams.Average(e => e.Grade),
    PassedUsersNum = groupExams.Count(e => /* Some long and complicated calculation */),
    CompletionRate = 100 * groupExams.Count(e => /* The same long and complicated calculation */) / TotalUsersNum
};

What bothers me is the calculation expression which appears twice, for PassedUsersNum and CompletionRate.

Assuming that CompletionRate = (PassedUsersNum / TotalUsersNum) * 100, how can I write it by reusing the calculation of PassedUsersNum, instead of writing that expression again?

Upvotes: 4

Views: 1040

Answers (2)

Paul
Paul

Reputation: 36339

You can also just extract your Count func into another method which returns a Func if you want, or a method that takes a double and returns a bool.

List<UserExams> listUserExams = GetUserExams();

var examData = 
from userExam in listUserExams
group by userExam.ExamID into groupExams
select new ExamData()
{
    ExamID = groupExams.Key,
    AverageGrade = groupExams.Average(funcMethod()),
    PassedUsersNum = groupExams.Count(e => traditionalMethod(e)),
    CompletionRate = 100 * groupExams.Count(e => /* The same long and complicated calculation */) / TotalUsersNum
};

// later...
private Func<double,bool> funcMethod(){ return e=> /* your calculation */ }

private bool traditionalMethod(double d){ return /* your calculation */ }

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1502106

The simplest way would be to use let to inject another selection step first:

List<UserExams> listUserExams = GetUserExams();

var examData = 
    from userExam in listUserExams
    group by userExam.ExamID into groupExams
    let passCount = groupExams.Count( /* long expression */)
    select new ExamData()
    {
        ExamID = groupExams.Key,
        AverageGrade = groupExams.Average(e => e.Grade),
        PassedUsersNum = passCount,
        CompletionRate = 100 * passCount / TotalUsersNum
    };

The expression will only be evaluated once per group, of course.

Upvotes: 6

Related Questions