sports
sports

Reputation: 8147

Two projections in same IQueryable

Is it possible to have two projections, .Select(...), in one same query?

        int total = ...;

        var sendersInfo = db.Persons
            .Select(p => new
            {
                sentSMS = p.SentSMS.Count(...large expression...),
            })
            // Calculate percentages
            .Select(i => new
            {
                sentSMS = i.sentSMS,
                percentage = i.sentSMS/total * 100
            });

The above is not working because apparently "i.sentSMS" isn't calculated yet and so a 0 (zero) is being used instead of the result.

What I'm trying to avoid is this (below), which does work, but has repeated code "...large expression...":

        int total = ...;

        var sendersInfo = db.Persons
            .Select(p => new
            {
                sentSMS = p.SentSMS.Count(...large expression...),
                percentage = p.SentSMS.Count(...large expression...) / total * 100
            });

Beside my question ("is it possible..."), is there a best way to achieve this? I don't like uggly code. Also I'm trying to achieve this in pure Linq-to-entities (no linq-to-objects)

Upvotes: 0

Views: 127

Answers (1)

Robert Synoradzki
Robert Synoradzki

Reputation: 2026

Commenting on your comment:

You do not need to cast to float every time - as soon as one of the operands is a float, the other is promoted, so the result is of type which bears a higher precision (float > int).

There are two arithmetic operations in line:

i.sentSMS / total * 100
//       (1)     (2)

Division, then multiplying. Both operators / and * have the same priority (refer to e.g. this), so the expression is evaluated from the left side, first computing the quotient (because both dividend and divisor are int, the result is also int), then multiplying the result with 100 (also int).

So, instead of doing

(float)i.sentSMS / (float)total * 100

It suffices to do:

//     ┌─ (float) thanks to casting
//     │           ┌─ (int) -> (float) promotion here, making the result of division a (float) too
//     │           │       ┌─ then 100 gets promoted in the second step the same way
(float)i.sentSMS / total * 100

And even shorter so:

// ┌─ (float) literal, because 100 is an (int) literal and 100.0 (or 100d) is a (double) literal
// │   ┌─ (int) -> (float) promotion
// │   │           ┌─ and again
100f * i.sentSMS / total

100f being a literal for float (same as (float)100, but neater) :)

In my opinion 100.0 looks even better, but it is a literal for double, even greater precision, so all the floats will get promoted to double, making the result double as well, and so you would get a compiler warning for losing precision while assigning double result to float variable.

Upvotes: 1

Related Questions