Reputation: 4003
We have the following definition LINQ:
myList.Select(s=> new DtoTest()
{
TotalSamples = myList.Count(c=> c.UserId == s.UserId),
EvaluatedSamples = myList.Count(c=> c.UserId == s.UserId && c.Status == Status.OK)
PercentageRealized = (myList.Count(c=> c.UserId == s.UserId) / myList.Count(c=> c.UserId == s.UserId && c.Status == Status.OK)) * 100
});
Is there a way where you can assign the property value "PercentageRealized" without using the same functions previously used in: "TotalSamples" & "EvaluatedSamples"?
Something like that:
myList.Select(s=> new DtoTest()
{
TotalSamples = myList.Count(c=> c.UserId == s.UserId),
EvaluatedSamples = myList.Count(c=> c.UserId == s.UserId && c.Status == Status.OK)
PercentageRealized = (TotalSamples / EvaluatedSamples) * 100 //<-!Not possible!
});
Any other tips?
Upvotes: 1
Views: 813
Reputation: 4950
If you were using an anonymous type this would be more complicated, but since DtoTest
is a class, you could always move your math into the property.
public class DtoTest
{
public float PercentageRealized
{
get { return (TotalSamples / EvaluatedSamples) * 100; }
}
}
Upvotes: 3
Reputation: 26917
What you are doing seems extremely questionable to me - you are making multiple passes over the source data to re-compute the same thing over and over again (per occurrence of UserId
), when it seems like what you should want is to compute once per UserId
, like so:
var ans2 = myList.GroupBy(s => s.UserId)
.Select(sg => {
var ts = sg.Count();
var es = sg.Count(c => c.Status == Status.OK);
return new DtoTest { UserId = sg.Key, TotalSamples = ts, EvaluatedSamples = es, PercentageRealized = (int)(100.0 * ts / es) };
});
Also, your percentage calculation will use C# integer division and not be close to correct, unless you convert to double
first. You can cast back to int
after the math.
If you really intended to return multiple results, and want to be efficient (and since I love extension methods), creating an extension method to count chained predicates for one pass:
public static class IEnumerableExt {
public static (int Cond1Count, int Cond2Count) Count2Chained<T>(this IEnumerable<T> src, Func<T, bool> cond1, Func<T, bool> cond2) {
int cond1Count = 0;
int cond2Count = 0;
foreach (var s in src) {
if (cond1(s)) {
++cond1Count;
if (cond2(s))
++cond2Count;
}
}
return (cond1Count, cond2Count);
}
}
Now you can count the sub-values in one pass and compute the third value:
var ans3 = myList.Select(s => {
var (ts, es) = myList.Count2Chained(c => c.UserId == s.UserId, c => c.Status == Status.OK);
return new DtoTest { UserId = s.UserId, TotalSamples = ts, EvaluatedSamples = es, PercentageRealized = (int)(100.0 * ts / es) };
});
Of course, depending on the size of myList
, you may still be better off calculating each answer once and then repeating them for the final answer:
var ansd = myList.GroupBy(s => s.UserId)
.Select(sg => {
var ts = sg.Count();
var es = sg.Count(c => c.Status == Status.OK);
return new { sg.Key, ts, es };
})
.ToDictionary(ste => ste.Key, ste => new DtoTest {
UserId = ste.Key,
TotalSamples = ste.ts,
EvaluatedSamples = ste.es,
PercentageRealized = (int)(100.0 * ste.ts / ste.es) });
var ans4 = myList.Select(s => ansd[s.UserId]);
Upvotes: 1
Reputation: 56433
first project to a tuple then project to your custom object:
myList.Select(s => (tSample: myList.Count(c=> c.UserId == s.UserId),
eSample : myList.Count(c=> c.UserId == s.UserId && c.Status == Status.OK)))
.Select(x => new DtoTest
{
TotalSamples = x.tSample,
EvaluatedSamples = x.eSample,
PercentageRealized = (x.tSample / x.eSample) * 100
});
or use an anonymous type:
myList.Select(s => new
{
tSample = myList.Count(c=> c.UserId == s.UserId),
eSample = myList.Count(c=> c.UserId == s.UserId && c.Status == Status.OK)
})
.Select(x => new DtoTest
{
TotalSamples = x.tSample,
EvaluatedSamples = x.eSample,
PercentageRealized = (x.tSample / x.eSample) * 100
});
Upvotes: 1
Reputation: 247153
Change the function delegate to use the already calculated values
myList.Select(s => {
var result = new DtoTest() {
TotalSamples = myList.Count(c => c.UserId == s.UserId),
EvaluatedSamples = myList.Count(c => c.UserId == s.UserId && c.Status == Status.OK)
};
result.PercentageRealized = (result.TotalSamples / result.EvaluatedSamples) * 100;
return result;
});
Upvotes: 3
Reputation: 68932
If PercentageRealized
is that simple calculations, why not have the calculation in the property in the class, something like this inside the class DTOTest:
public float PercentageRealized => (TotalSamples / EvaluatedSamples) * 100;
Upvotes: 2