【问题标题】:Elegant way to retrieve the index of the max element from a List in C#从 C# 中的列表中检索最大元素索引的优雅方法
【发布时间】:2012-05-30 18:51:21
【问题描述】:

我正在发现 C# 提供的用于处理集合的工具。

假设我有一个元素列表,并且我想检索最满足某个属性的元素。基本上是elements.Max(predicate),除了我对最佳元素的索引感兴趣。我想要索引而不是元素本身的原因是可能没有这样的元素,并且类型不可为空。

编写这样的函数是微不足道的,但我有兴趣使用 C# 提供的工具的表现力来获得简洁清晰最优O(n))。

此时我有以下代码,它看起来仍然很混乱,并且对属性进行了两次评估。

List<foo> elements;
private int getBest(object x)
{
  var indices = Enumerable.Range(0, elements.Count);
  return indices.Aggregate(-1, (best, next) =>
    (-1 == best || eval(x, elements[next]) > eval(x, elements[best])) ? next : best);
}

如何让这段代码变得更好?


附录:为了清楚起见,我没有把它放在代码中,但是如果eval() 低于某个阈值,则该元素将被丢弃。

【问题讨论】:

  • 你应该可以缓存最好的分数和它的索引,所以你只需要做一次属性评估。
  • 我想你会找到你的答案here
  • 大概不可能有这样一个元素的唯一方法是让列表为空,还是你的意思是别的?
  • @AakashM:我编辑了有关您评论的问题。

标签: c# collections


【解决方案1】:

我建议将SelectAggregate LINQ 扩展方法结合使用。使用Select 方法,您可以创建一个匿名类型,其中包含您集合中每个项目的indexvalue。然后使用 LINQ Aggregate 方法,您可以缩小具有最大价值的项目。我认为这样的一些应该可以工作:

private int GetIndexOfHighestValue(IEnumerable<int> list)
{
    return list.Select((i, v) => new { Index = i, Value = v })
        .Aggregate((a, b) => (a.Value > b.Value) ? a : b)
        .Index;
}

【讨论】:

  • 肯定比我的代码 sn-p 干净多了,谢谢! (但仍然存在双重评估问题)
  • 忘记我对评估的评论。应用于我的示例,在Select 部分我们有Value = eval(x, v),我们很高兴。再次感谢!
【解决方案2】:

用 LINQ 做这件事很有趣,但不用 LINQ 做起来更直观:

int bestIndex = -1;
int bestResult = -1;
for(int i = 0; i < elements.Count; ++i)
{
    int currentResult = eval(x, elements[i]);
    if (currentResult > bestResult)
    {
        bestResult = currentResult;
        bestIndex = i;
    }
}

【讨论】:

  • 这是我想要避免的。写起来很容易,但是阅读和弄清楚需要一些时间。我正在寻找该语言提供的写一些非常优雅和清晰的东西,例如var best = elements.MaxBy(y =&gt; eval(x, y))
  • 你可以做elements.Max(e =&gt; eval(x, e)),但这总是会产生一个元素。我会将 eval 拆分为一些过滤方法和实际计算。这样你就可以做elements.Where(e =&gt; qualifies(e)).Max(e =&gt; eval(x, e)) 例如。
  • @JulienGuertault 用合适的扩展方法包装它,你就完成了。请注意,您的示例不是“语言提供”的东西 - 它是 library 提供的东西。该库的内部结构看起来像这段代码。
【解决方案3】:

类似这可以工作

// OOPS: This won't work because Max is defined the way it is. Always bugged me...
var result = elements.Select((e, i) => new {Element = e, Index = i}).Max(x => x.Element).Select(x => x.Index);

哦,老鼠。正确的。这行不通。所以:让我们拿出我们的多面手:Aggregate。我们开始:

var elements = new List<int>{1, 7, 2, 5};
var result = elements.Select((e, i) => new {Element = e, Index = i})
    .Aggregate(
        new { Element = elements.First(), Index = -1}, // gotta start somewhere and Element is non-nullable according to OP
        (max, next) => (max.Element > next.Element) && max.Index >= 0 ? max : next,
        max => max.Index);

这导致1。这有帮助吗?

【讨论】:

  • 不,它不起作用。 .Max 扩展方法直接返回最大元素。不再有 Index 属性,因此您的最终 .Select 语句将无法编译。 .Select 扩展方法在IEnumerable&lt;T&gt; 上运行,但.Max 返回T
猜你喜欢
  • 2019-09-18
  • 2018-07-09
  • 2014-09-19
  • 2011-04-01
  • 2017-06-06
  • 1970-01-01
  • 1970-01-01
  • 2021-06-20
  • 2018-12-18
相关资源
最近更新 更多