Home > Software design >  OrderBy() a List<> property
OrderBy() a List<> property

Time:03-29

I have a problem to order a list after a certain condition. The situation is that i build a Competition application for Range shooting and when i am about to order the list of results there is on condition i dont know how to handle with OrderBy().

This is the classes i work with, simplified to only include properties important for this question, and the Shot class is no point in showing here. I have a List of ShooterSignup that i work with and try to order:

ShooterSignup

public class ShooterSignup
{
    public List<Serie> Series { get; set;}
}

Serie

public class Serie
{
    public List<Shot> Shots { get; set;}
}

The Conditions i have to order the results by are:

  1. Total score of all series
  2. Number of Center hits

These two OrderBy are straight forward and no problem. But the next OrderBy step is where i cant get it to work. The next step to order by is:

  1. Highest score on last Serie
  2. Highest score on next to last serie
  3. Highest score on the one before that one
  4. ......

and so on for the number of series shot this competition. How can i achive this orderby? Is it even possible?

Edit: This is done in a query to entity framework, so i cant use any methods created on the classes themselves since entity framework will not know how to translate that into a stored procedure

CodePudding user response:

Let the classes are defined as follows:

public class ShooterSignup
{
    public List<Serie> Series { get; set; }

    public int GetTotalScore()
    {
        return Series.Sum(serie => serie.GetScore());
    }

    public int GetCenterHits()
    {
        return Series.Sum(serie => serie.GetCenterHits());
    }
}

public class Serie
{
    public List<Shot> Shots { get; set; }

    public int GetScore()
    {
        return Shots.Sum(shot => shot.Score);
    }

    public int GetCenterHits()
    {
        // assume that center hit is a hit with score = 10
        return Shots.Count(shot => shot.Score == 10);
    }
}

public class Shot
{
    public Shot(int score)
    {
        Score = score;
    }
    public int Score { get; set; }
}

You can define series-wise comparer like this:

Comparer<ShooterSignup> seriesComparer = Comparer<ShooterSignup>.Create((a, b) =>
{
    using (var enumeratorA = Enumerable.Reverse(a.Series).GetEnumerator())
    using (var enumeratorB = Enumerable.Reverse(b.Series).GetEnumerator())
    {
        bool moveNextA = enumeratorA.MoveNext();
        bool moveNextB = enumeratorB.MoveNext();

        while (moveNextA && moveNextB)
        {
            int scoreA = enumeratorA.Current.GetScore();
            int scoreB = enumeratorB.Current.GetScore();
            if (scoreA != scoreB)
            {
                return scoreB - scoreA;
            }

            moveNextA = enumeratorA.MoveNext();
            moveNextB = enumeratorB.MoveNext();
        }

        if (!moveNextA && !moveNextB)
        {
            return 0;
        }

        return moveNextA ? 1 : -1;
    }
});

Now your can sort list of ShooterSignup:

var orderedSignups = signups
    .OrderBy(s => s.GetTotalScore())
    .ThenBy(s => s.GetCenterHits())
    .ThenBy(s => s, seriesComparer)
    .ToList();
  • Related