【问题标题】:Better way to return an object by max() in LINQ在 LINQ 中通过 max() 返回对象的更好方法
【发布时间】:2013-09-12 14:06:45
【问题描述】:

我目前正在学习 C# 中的 LINQ,想知道是否有更好的方法可以在 LINQ 语句中使用 Max() 函数返回对象。

这是我的用户类:

public class User
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public double MonthlyWage { get; set; }
    }

这是我的表填充类:

public class UsersTable
    {
        public IList<User> Populate()
        {
            IList<User> Users = new List<User>()
            {
                new User{ID = 1, Name = "Bob", MonthlyWage = 1200.00},
                new User{ID = 2, Name = "Lee", MonthlyWage = 2200.00},
                new User{ID = 3, Name = "Dan", MonthlyWage = 3200.00},
                new User{ID = 4, Name = "Liam", MonthlyWage = 4200.00},
                new User{ID = 5, Name = "Danny", MonthlyWage = 4213.00},
                new User{ID = 6, Name = "Jonathan", MonthlyWage = 1222.00},
                new User{ID = 7, Name = "Martin", MonthlyWage = 1233.00},
                new User{ID = 8, Name = "Dec", MonthlyWage = 9999.99}
            };
            return Users;
        }
    }

这里是主要方法:

class Program
    {
        static void Main(string[] args)
        {
            UsersTable UserTable = new UsersTable();
            IList<User> Users = UserTable.Populate();

            double max = Users.Max(x => x.MonthlyWage);
            var maxMonthlyWage = Users
                .Where(m => m.MonthlyWage == max)
                .Select(x => x);

            foreach (var item in maxMonthlyWage)
            {
                Console.WriteLine("{0}: {1} {2} MAX", item.ID, item.Name, item.MonthlyWage);
            }

            Console.ReadLine();
    }

有没有一种方法可以在不事先创建double max 的情况下返回月工资最高的用户?这是执行此类查询的最佳方式吗?

【问题讨论】:

  • 您不需要.Select(x =&gt; x) - 它完全是多余的。 Where 方法将为您提供所需的内容。只有在需要更改返回的内容时才应使用 select:.Select(x =&gt; new SomethingElse { Something = x })
  • 谢谢@Dan 我不知道
  • 从您的原始代码中,我了解到您期望不止一个 Max。价值发生(这就是我不依赖 First 的原因)。如果是这种情况,Tommy Grovnes 的方法并不能提供您想要的,因为总是只输出一次。正如 MarcinJuraszek 正确指出的那样,我的原始代码效率太低了。我已经纠正了它,现在好多了。在任何情况下,它的效率都比您原始代码中的效率差(甚至可以通过不依赖 LINQ 进一步提高)。

标签: c# asp.net linq


【解决方案1】:

一个班轮

  var item = Users.OrderByDescending(x => x.MonthlyWage).FirstOrDefault();

  if(item != null)
    Console.WriteLine("{0}: {1} {2} MAX", item.ID, item.Name, item.MonthlyWage);

  Console.ReadLine();

如果我们想要所有高收入者:

var wageGroups = from u in Users
                group u by u.MonthlyWage into ug
                orderby ug.Key descending
                select new { MonthlyWage = ug.Key, Users = ug.ToList() };

var topEarners = wageGroups.First().Users;

foreach (var item in topEarners)
{
    Console.WriteLine("{0}: {1} {2} MAX", item.ID, item.Name, item.MonthlyWage);
}

Console.ReadLine();

【讨论】:

  • OP 似乎预计不止一次发生。
  • 好的。但我不是那个意思。我的意思是 First()(或 FirstOrDefault())只返回一个值;但是来自 OP 的原始代码似乎表明他期望出现不止一次(foreach(maxMonthlyWage 中的 var 项))-> 不止一种情况具有最大值。价值。这就是我没有使用 First 的原因;但我已经向 OP 强调了这一点,他似乎并不关心这一点,所以我猜我的假设是错误的,他实际上只期望一个值。
  • 我最初误解了这个问题,只是想我会把这两个例子都留在那里以供参考。不错的收获
【解决方案2】:

你可以把所有东西放在一起:

var maxMonthlyWage = Users
                    .OrderByDescending(x => x.MonthlyWage)
                    .TakeWhile(x => x.MonthlyWage == Users.Max(y => y.MonthlyWage))
                    .ToList();

注意:我刚刚回答了 OP 关于删除中间变量的问题(也删除了一些冗余位)。无论如何,我不想被误解:从效率的角度来看,所提出的方法并不比 OP 的方法好。

注意 2:正如 MarcinJuraszek 强调的那样,此查询执行了两次分析。他提出了一个外部库来避免这种情况(moreLINQ)。其他选项可能依赖First(由 Tommy Grovnes 提议),尽管这只会产生一个结果(不太可能是 OP 似乎正在寻找的)。

注意 3:正如 MarcinJuraszek 正确强调的那样,原始 O​​P 的代码只迭代一次以计算最大值。我的答案的新版本(比最初的更好)仍然迭代不止一次,因此效率低于原始版本。尽管如此,OP 要求删除中间变量,这就是这个答案的原因。

【讨论】:

  • 效率低下,因为需要 2 次遍历集合。
  • @MarcinJuraszek 如何改进?
  • 使用来自moreLINQlibrary 的MaxBy,或者如果你不想在你的代码中包含整个moreLINQ,可以编写自定义扩展方法来处理。
  • @MarcinJuraszek 老实说,我不喜欢过多地依赖 LINQ(而较少依赖外部库)。但是 OP 询问如何删除中间变量,这就是我所做的。我会用这些想法更新我的答案。
  • 恐怕您的解决方案实际上是O(n^2)。在Where 之前计算max 使其只是一个int,但将其作为谓词的一部分传递将导致Max() 方法调用每个集合元素。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-30
  • 1970-01-01
相关资源
最近更新 更多