MBerg
MBerg

Reputation: 41

Ordering a list by most occurrences of lowest number

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

Answers (4)

Dinesh Singh
Dinesh Singh

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

Misiakw
Misiakw

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

Rawling
Rawling

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

gmn
gmn

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

Related Questions