【问题标题】:Find item in Enumerable with the maximum property value在 Enumerable 中查找具有最大属性值的项目
【发布时间】:2014-05-10 05:17:47
【问题描述】:

我正在用 C# 编写一个 AI 竞赛的条目,并且我正在寻找一种更优雅的方式来搜索项目。 (我更熟悉嵌入式 C 编程,但我更喜欢 C# 来参加 AI 竞赛。)

比赛服务器使用dmcs编译参赛作品,即.Net framework 4.0;我正在使用 Visual Studio Express 2013 进行测试。

我正在尝试在列表中搜索具有最大参数值的项目,该参数也满足某个先决条件。我不想要最大值,但我想要具有最大值的项目。

这是我的原始代码,它使用 foreach 循环执行我想要的操作:

List<Region> myList = new List<Region>();

// ...
// myList gets populated with elements
// ...

Region biggest = null;
int biggestSize = -1;

foreach (Region r in myList)
{
    // We only want elements that are eligible for expansion
    if (r.EligibleForExpansion())
    {
        if (r.Size > biggestSize)
        {
            biggest = r;
            biggestSize = r.Size;
        }
    }
}

return biggest; // I want the biggest Region, not the Size of the biggest region.

我正在尝试找到一种更优雅的方法来执行此操作,因此我的代码中没有 foreach 循环。我试过这个:

return myList.Max(delegate(Region r) { if (r.EligibleForExpansion()) return r.Size; else return -1; });

但是,这会返回最大区域的 Size 值,而不是最大区域本身(这是我需要的)。

我知道如果没有满足要求的区域,我的 foreach 代码将返回 null,而 Max 代码将返回 -1(或任何不满足要求的区域);我可以处理任何一种方式。

不过,我认为我不能只使 Region IComparable;我对Region对象的搜索次数很多,而且我需要在不同的时间按不同的参数进行排序,所以不同的搜索比较功能会有所不同。

我可以将我的 foreach 代码包装在一个静态函数中,然后在我需要搜索的任何地方调用它,但似乎在 C# 中应该有一种更优雅的方式来执行此操作。

【问题讨论】:

    标签: c# foreach max enumerable


    【解决方案1】:

    您可以为此目的使用开箱即用的Aggregate

            var item = myList
                .Where(r => r.EligibleForExpansion())
                .Aggregate((Region)null, (max, cur) => (max == null ? cur : cur.Size > max.Size ? cur : max));
    

    如果Region 是一个值类型(它不是),您可以将初始值包装在一个可空值中,并为一个空列表获取一个空值:

            var item = myList
                .Where(r => r.EligibleForExpansion())
                .Aggregate((Region?)null, (max, cur) => (max == null ? cur : cur.Size > max.Value.Size ? cur : max));
    

    【讨论】:

      【解决方案2】:

      这个怎么样?

      myList.Where(r => r.EligibleForExpansion).OrderBy(r => r.Size).LastOrDefault()
      

      【讨论】:

      • 排序是O(n*logn),而问题本身是线性的。
      • 没错,这是一个低效的解决方案。它唯一的优点是简洁,考虑到 morelinq 可以简单地导入和忘记,这是微不足道的。
      【解决方案3】:

      使用MaxBy from moreLINQ library:

      public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source,
          Func<TSource, TKey> selector)
      {
          return source.MaxBy(selector, Comparer<TKey>.Default);
      }
      
      public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source,
          Func<TSource, TKey> selector, IComparer<TKey> comparer)
      {
          if (source == null) throw new ArgumentNullException("source");
          if (selector == null) throw new ArgumentNullException("selector");
          if (comparer == null) throw new ArgumentNullException("comparer");
          using (var sourceIterator = source.GetEnumerator())
          {
              if (!sourceIterator.MoveNext())
              {
                  throw new InvalidOperationException("Sequence contains no elements");
              }
              var max = sourceIterator.Current;
              var maxKey = selector(max);
              while (sourceIterator.MoveNext())
              {
                  var candidate = sourceIterator.Current;
                  var candidateProjected = selector(candidate);
                  if (comparer.Compare(candidateProjected, maxKey) > 0)
                  {
                      max = candidate;
                      maxKey = candidateProjected;
                  }
              }
              return max;
          }
      }
      

      像这样:

      var item = myList.Where(x => x.EligibleForExpansion())
                       .MaxBy(x => x.Size);
      

      【讨论】:

        猜你喜欢
        • 2016-06-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-05-07
        • 1970-01-01
        • 1970-01-01
        • 2013-02-22
        • 1970-01-01
        相关资源
        最近更新 更多