I have the following data structure:
public class Difference
{
public Difference() { }
public Differential Type { get; set; }
public int Magnitude { get; set; }
}
public enum Differential
{
low,
middle,
high
}
I may have the following data:
Magnitude Type
4456 low
4456 low
4423 low
4421 low
1000 high
1000 high
1001 high
1560 middle
4456 low
4421 low
Im trying to return a dataset that gives me the Count on the number identical maginitudes and their types BUT ONLY THE TOP IN EACH TYPE in descending order of count so for the above (in order)
Value: 4456 Type: Low Count: 3
Value: 1000 Type: High Count: 2
Value: 1560 Type: Middle Count: 1
Notice how 4421 at a count of 2 wasn’t in there because 4456 had a larger count?
i think i have got close but not quite there:
var query = (from value in differences
group value by new {value.Magnitude, value.Type} into groupjoin
select new
{
Value = groupjoin.Key.Magnitude,
Type = groupjoin.Key.Type,
Count = groupjoin.Count()
})
.OrderByDescending(v => v.Count)
.ThenByDescending(v => v.Value).ThenByDescending(v => v.Type).ToList();
UPDATE
Based on what was suggested below by Douglas ive managed to implement it with the following. Im not too concerned though if there are ties, i would like to return just the First() one (for each type) if possible. i can only do this by looping:
var query = (from value in differences
/*where value.type == Differential.low*/
group value by new {value.Magnitude, value.Type} into groupjoin
select new
{
Value = groupjoin.Key.Magnitude,
Type = groupjoin.Key.Type,
Count = groupjoin.Count()
});
var query2 = query.Where(g1 => !query.Any(g2 =>
g2.Type == g1.Type &&
g2.Count > g1.Count));
int highest = 0;
int lowest = 0;
int middle = 0;
foreach (var item in query2)
{
if (item.Type == Differential.high && item.Count > highest)
{
oresult.numberOfHighHits = item.Count;
oresult.highTimeMagnitude = item.Value;
}
if (item.Type == Differential.low && item.Count > lowest)
{
oresult.numberOfLowHits = item.Count;
oresult.lowTimeMagnitude = item.Value;
}
if (item.Type == Differential.middle && item.Count > middle)
{
oresult.numberOfMiddleHits = item.Count;
oresult.middleTimeMagnitude = item.Value;
}
}
I’m preserving the first part of your LINQ query that gives the groupings of distinct magnitude–type pairs (and their counts):
Then, I’m writing another LINQ which takes all the groupings produced by the first query and, for each grouping
g1, checks that there does not exist any other groupingg2which has the same type and a larger count. This would ensure that only the groupings with the largest count of their type are returned. In the case of ties (multiple groupings of the same type have the same largest count), all top groupings are returned.Edit: To return a single result for each type even in the case of ties, we could add another condition to our
query2filter such that, if two groupings have the sameTypeand the sameCount, then the one with the largerValue(originally namedMagnitude) is picked. If you would prefer to pick the one with the smallerValue, just replace the last>with a<.This approach would make the results of your query deterministic (rather than arbitrarily dependent on, for example, which grouping occurs first in the original list).