Reputation: 41
I've been playing with this for quite some time now but couldn't figure it out or find an existing question about it. I'm writing a C# tool for race scoring where I need to make an overall result. For this result some special rules apply which I can't figure out the best way to do it:
I've got a list with race results (Winner gets 0 points, 2nd gets 2, 3rd gets 3 etc..). In order to make the results work we first order by a sum of all the points. When that's done there is still a possibility drivers share the same amount of points. In that case we have to look at their best results, for example:
driver 1 has scored: 0 + 0 + 4 = 4 driver 2 has scored: 2 + 2 + 0 = 4
In this case driver 1 would be first because he has the most wins, driver 2 will follow because he has only one win. This should apply for all the participants.
Update: The data model I have looks as following:
public class SerieResultForPilot {
public long Position { get; set; }
public Pilot Pilot { get; set; }
public List<SerieResultEntry> SerieResultEntries { get; set; }
public long Total { get; set; }
}
public class SerieResultEntry {
public long Points { get; set; }
public long Penalties { get; set; }
}
The ordering I currently have but doesn't work the right way:
var orderedList = serieResultList.OrderBy(rfp => rfp.Total);
for (int i = 0; i < pilots.Count; i++) {
orderedList = orderedList.ThenBy(c => c.SerieResultEntries.Count(sre => sre.Points == i));
}
I hope anyone can help me with a solution for this issue since I couldn't find an issue like this on here yet.
Thanks in advance, Mark
Solution Compare function
public class SerieResultForPilot {
public long Position { get; set; }
public Pilot Pilot { get; set; }
public List<SerieResultEntry> SerieResultEntries { get; set; }
public long Total { get; set; }
public long CompareTo(SerieResultForPilot other) {
var result = this.Total - other.Total;
if (result != 0) {
return result;
}
var thisResults = this.SerieResultEntries.OrderBy(x => x.Points).Select(x => x.Points).ToArray();
var otherResults = other.SerieResultEntries.OrderBy(x => x.Points).Select(x => x.Points).ToArray();
for (var i = 0; i < thisResults.Length; i++) {
if (thisResults[i] != otherResults[i]) {
return thisResults[i] - otherResults[i];
}
}
return 0;
}
}
public class SerieResultEntry {
public long Points { get; set; }
public long Penalties { get; set; }
}
Ordering
var orderedList = serieResultList.OrderBy(rfp => rfp.Total);
foreach (var result in serieResultList) {
orderedList = orderedList.ThenBy(c => c.CompareTo(result));
}
Upvotes: 2
Views: 79
Reputation: 210
You need to first find no. of wins and sum of score of each drivers and then sort records based on their score and win as show in below example:
public class LinqUtil<T> where T : BaseClass
{
public static void MyScore()
{
List<DriverScore> scores = new List<DriverScore>
{
new DriverScore {driverId="driver2",score=2 },
new DriverScore {driverId="driver2",score=2 },
new DriverScore {driverId="driver2",score=0 },
new DriverScore {driverId="driver1",score=0 },
new DriverScore {driverId="driver1",score=0 },
new DriverScore {driverId="driver1",score=4 },
};
var _score = (from s in scores
group s by s.driverId into g
select new
{
driverId = g.Key,
Win = g.Count(x => x.score == 0),
Score = g.Sum(x => x.score)
}).OrderByDescending(x=>x.Score).ThenByDescending(x=>x.Win);
foreach(var _s in _score)
{
Console.WriteLine(_s.driverId + " " + _s.Win + " " + _s.Score);
}
}
}
Upvotes: 0
Reputation: 922
you may implement IComparable
interface in class build around result.
according to your data model compairing method may look something like that
public int CompareTo(SerieResultForPilot other){
var result this.Total - other.Total;
if (result != 0){
return result;
}else{
var thisResults = this.SerieResultEntries.OrderBy(x => x.SerieResultEntries.Points).toArray();
var otherResults = his.SerieResultEntries.OrderBy(x => x.SerieResultEntries.Points).toArray();
for (var i=0; i< thisResults.Count; i++){
if (thisResults[i] != otherResults[i]){
return thisResults[i] - otherResults[i];
}
}
return 0;
}
}
documentation of IComparable
interface is available here https://msdn.microsoft.com/pl-pl/library/system.icomparable(v=vs.110).aspx
Upvotes: 2
Reputation: 50114
Looking at your code, I think all you need to do is replace ThenBy
with ThenByDescending
. (You want drivers with most low scores at the top of the list.) It also relies on the maximum race score being less than the number of drivers - maybe you need a <=
rather than <
in your for
loop?
It's not the most efficient way of doing it, but it's pretty clever - I'd struggle to come up with something that used fewer lines of code.
Upvotes: 0
Reputation: 4319
Perhaps not the most efficient, but I think this would do it:
drivers.OrderBy(x => new { x.Sum(y => y.Points),
x.Sum(y => y.Points.Where(t => t.Points = 4)), x.Sum(y => y.Points.Where(t => t.Points = 3)) });
Assuming points is an Enumerable stored for each driver.
Upvotes: 0