带有 subselect 和 groupby 的 LINQ 仅获取列表中每个项目的最新版本

发布于 2024-07-12 22:44:04 字数 1400 浏览 5 评论 0原文

我是 LINQ 的新手...

我有一个 IEnumerable 通用列表,其中包含不同版本的答案(每个版本都有一个问题的 FK)。 从这个列表中我只需要获取最新版本答案的字典。

一个非常简化的类图:

问题
-ID
-问题
- ...其他属性

答案
-ID
-版本
-问题ID
-价值
- ...其他属性

目前我有以下属性:

IEnumerable<Answer> answers = GetAnswers();

IDictionary<long, AnswerDTO> latestVersionAnswers = new Dictionary<long, AnswerDTO>();
if (answers != null)
{
latestVersionAnswers = answers
      .OrderBy(e => e.ID)
      .GroupBy(e => e.Question.ID)
      .Select(g => new AnswerDTO
                        {
                             Version = g.Last().Version, // g.Select(e => e.Version).Max(), 
                             QuestionID = g.Key,
                             ID = g.Last().ID,
                             Value = g.Last().Value
                                 }).ToDictionary(c => c.QuestionID);
        }

虽然这在大多数情况下都有效,但您很快就会发现它需要一些认真的优化(并且有点脆弱,因为它取决于应答记录行顺序而不是“最大”逻辑)。 使用 LINQ 执行此操作的最佳方法是什么,还是最好为每个循环执行多个?

  1. 如果我只需要版本(而不是 ID、值等),我就不需要 OrderBy,因为我可以直接 g.Select(e => e.Version).Max() (或者我已经现在看到 C# List<> GroupBy 2 Values 上的帖子,但这又会只返回密钥和一个属性:版本)。
  2. 最终,在这种特殊情况下,我更愿意“过滤”原始列表并返回原始答案项目,而不是涉及 AnswerDTO。

任何指示或帮助将不胜感激!

I'm a newbie when it comes to LINQ...

I have an IEnumerable generic list that contains answers with different versions (each with an FK to the question). From this list I need to get a dictionary of latest version answers only.

A very simplified class diagram:

Question
-ID
-question
- ...other properties

Answer
-ID
-Version
-QuestionID
-Value
- ...other properties

Currently I've got the following :

IEnumerable<Answer> answers = GetAnswers();

IDictionary<long, AnswerDTO> latestVersionAnswers = new Dictionary<long, AnswerDTO>();
if (answers != null)
{
latestVersionAnswers = answers
      .OrderBy(e => e.ID)
      .GroupBy(e => e.Question.ID)
      .Select(g => new AnswerDTO
                        {
                             Version = g.Last().Version, // g.Select(e => e.Version).Max(), 
                             QuestionID = g.Key,
                             ID = g.Last().ID,
                             Value = g.Last().Value
                                 }).ToDictionary(c => c.QuestionID);
        }

While this works for the most part, you can quickly see that it needs some serious optimization (and is a little fragile in that it depends on the Answer record rows order instead of "Max" logic). What would be the best way to do this with LINQ, or is it best to just do multiple for each loops?

  1. If I only needed the version (and not the ID, Value, etc.) I wouldn't need the OrderBy as I could just go g.Select(e => e.Version).Max() (or I've now seen the post at C# List<> GroupBy 2 Values, but this again would only return the key/s and one property: Version).
  2. Ultimately, in this particularly situation I would much prefer to just "filter" the original list and return the original answer items instead of involving the AnswerDTO.

Any pointers or help would be much appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

梦里人 2024-07-19 22:44:04

怎么样....

   private void button1_Click(object sender, EventArgs e)
    {
      List<Answer> list = GetAnswers();

      var dict = (from a in list
                 group a by a.QuestionID into grp
                 from g in grp
                 where g.Version == grp.Max(m => m.Version)
                 select new { id = g.QuestionID, q = g }).ToDictionary( o => o.id, o => o.q);

      StringBuilder sb = new StringBuilder();
      foreach (var elem in dict)
      {
        sb.AppendLine(elem.Key.ToString() + "-" + elem.Value.Version.ToString());
      }
      MessageBox.Show(sb.ToString());
    }

    private List<Answer> GetAnswers()
    {
      List<Answer> result = new List<Answer>();
      result.Add(new Answer() { ID = 1, QuestionID = 1, Version = 1 });
      result.Add(new Answer() { ID = 2, QuestionID = 1, Version = 2 });
      result.Add(new Answer() { ID = 3, QuestionID = 1, Version = 3 });
      result.Add(new Answer() { ID = 4, QuestionID = 2, Version = 1 });
      result.Add(new Answer() { ID = 5, QuestionID = 2, Version = 2 });
      result.Add(new Answer() { ID = 6, QuestionID = 2, Version = 3 });
      result.Add(new Answer() { ID = 7, QuestionID = 3, Version = 1 });
      result.Add(new Answer() { ID = 8, QuestionID = 3, Version = 2 });
      result.Add(new Answer() { ID = 9, QuestionID = 3, Version = 3 });
      result.Add(new Answer() { ID = 10, QuestionID = 3, Version = 4 });
      return result;
    }

How about something like....

   private void button1_Click(object sender, EventArgs e)
    {
      List<Answer> list = GetAnswers();

      var dict = (from a in list
                 group a by a.QuestionID into grp
                 from g in grp
                 where g.Version == grp.Max(m => m.Version)
                 select new { id = g.QuestionID, q = g }).ToDictionary( o => o.id, o => o.q);

      StringBuilder sb = new StringBuilder();
      foreach (var elem in dict)
      {
        sb.AppendLine(elem.Key.ToString() + "-" + elem.Value.Version.ToString());
      }
      MessageBox.Show(sb.ToString());
    }

    private List<Answer> GetAnswers()
    {
      List<Answer> result = new List<Answer>();
      result.Add(new Answer() { ID = 1, QuestionID = 1, Version = 1 });
      result.Add(new Answer() { ID = 2, QuestionID = 1, Version = 2 });
      result.Add(new Answer() { ID = 3, QuestionID = 1, Version = 3 });
      result.Add(new Answer() { ID = 4, QuestionID = 2, Version = 1 });
      result.Add(new Answer() { ID = 5, QuestionID = 2, Version = 2 });
      result.Add(new Answer() { ID = 6, QuestionID = 2, Version = 3 });
      result.Add(new Answer() { ID = 7, QuestionID = 3, Version = 1 });
      result.Add(new Answer() { ID = 8, QuestionID = 3, Version = 2 });
      result.Add(new Answer() { ID = 9, QuestionID = 3, Version = 3 });
      result.Add(new Answer() { ID = 10, QuestionID = 3, Version = 4 });
      return result;
    }
暖风昔人 2024-07-19 22:44:04
latestVersionAnswers = answers
  .GroupBy(e => e.Question.ID)
  .Select(g => g.OrderByDescending(e => e.Version).First())
  .ToDictionary(e => e.Question.ID);

或者,如果您更喜欢 ToDictionary 的选择重载:

latestVersionAnswers = answers
  .GroupBy(e => e.Question.ID)
  .ToDictionary(
    g => g.Key,
    g => g.OrderByDescending(e => e.Version).First()
  );
latestVersionAnswers = answers
  .GroupBy(e => e.Question.ID)
  .Select(g => g.OrderByDescending(e => e.Version).First())
  .ToDictionary(e => e.Question.ID);

Or, if you prefer the selecting overload of ToDictionary:

latestVersionAnswers = answers
  .GroupBy(e => e.Question.ID)
  .ToDictionary(
    g => g.Key,
    g => g.OrderByDescending(e => e.Version).First()
  );
痴者 2024-07-19 22:44:04

我只需重写数据模型以包含当前的答案版本 ID。

I would just rewrite the data model to include the current answer version ID.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文