OrderBy() 列表<> 属性

OrderBy() a List<> property

我在满足特定条件后无法订购列表。情况是我构建了一个用于射程射击的竞赛应用程序,当我要订购结果列表时,我不知道如何使用 OrderBy() 进行处理。

这是我使用的 classes,简化为仅包含对这个问题很重要的属性,镜头 class 没有必要在这里显示。我有一份 ShooterSignup 列表,我正在使用并尝试订购:

ShooterSignup

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

意甲

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

我必须按以下条件对结果进行排序:

  1. 所有系列的总分
  2. 中心命中数

这两个OrderBy很直接,没问题。但是下一个 OrderBy 步骤是我无法让它工作的地方。下一步的订购方式是:

  1. 上届意甲联赛的最高分
  2. 仅次于上一场意甲的最高分
  3. 前一项得分最高
  4. ......

以此类推本次比赛拍摄的连续剧数量。我怎样才能达到这个顺序?有可能吗?

编辑: 这是在对 entity framework 的查询中完成的,所以我不能使用在 classes 上创建的任何方法,因为 entity framework 不知道如何将其转换为存储过程

让类定义如下:

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; }
}

您可以这样定义 series-wise 比较器:

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;
    }
});

现在您可以对 ShooterSignup 的列表进行排序:

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

更新。我不确定是否可以形成 LINQ to SQL query that returns ShooterSignup list in such an order。更简单的方法是从数据库中获取每个 Serie 的累计总数,然后在客户端进行排序。

您可以server-side这样查询:

var seriesTotals = context.Shots
    .GroupBy(shot => shot.SerieId)
    .Join(context.Series,
        g => g.Key,
        serie => serie.Id, (grp, serie) => new { grp, serie })
    .Join(context.ShooterSignup,
        t => t.serie.SignupId, signup => signup.Id,
        (t, signup) => new { t, signup })
    .OrderByDescending(t => t.t.serie.Id)
    .Select(total => new
    {
        SerieId = total.t.grp.Key,
        SignupId = total.signup.Id,
        SignupName = total.signup.Name,
        SerieScore = total.t.grp.Sum(s => s.Score),
        CenterHits = total.t.grp.Count(s => s.Score == 10)
    });

结果例如如下:

SerieId SignupId SignupName SerieScore CenterHits
1 1 A 99 8
2 2 B 95 6
3 1 A 90 3
4 2 B 94 5

现在您可以在客户端按射手分组和订购系列:

var series = seriesTotals.AsEnumerable()
    .GroupBy(s => s.SignupId)
    .Select(gr => new
    {
        gr.FirstOrDefault().SignupName,
        TotalScore = gr.Sum(s => s.SerieScore),
        CenterHits = gr.Sum(s => s.CenterHits),
        Scores = string.Join(",", gr.Select(s => s.SerieScore.ToString().PadLeft(3, '0'))) // aggregate all scores in a single string for each ShooterSignup
    })  
    .OrderByDescending(g => g.TotalScore)
    .ThenByDescending(g => g.CenterHits)
    .ThenByDescending(g => g.Scores)
    .ToList();

此查询将生成如下内容:

SignupName TotalScore CenterHits Scores
B 189 11 094,095
A 189 11 090,099