[C#] 如何使用LINQ获取索引?


Answers

myCars.Select((v, i) => new {car = v, index = i}).First(myCondition).index;

或略短一些

myCars.Select((car, index) => new {car, index}).First(myCondition).index;
Question

这个问题在这里已经有了答案:

给定一个这样的数据源:

var c = new Car[]
{
  new Car{ Color="Blue", Price=28000},
  new Car{ Color="Red", Price=54000},
  new Car{ Color="Pink", Price=9999},
  // ..
};

我怎样才能找到满足一定条件的第一辆汽车的指数

编辑:

我可以想到这样的事情,但它看起来很可怕:

int firstItem = someItems.Select((item, index) => new    
{    
    ItemName = item.Color,    
    Position = index    
}).Where(i => i.ItemName == "purple")    
  .First()    
  .Position;

用最简单的旧循环来解决这个问题会是最好的吗?




myCars.TakeWhile(car => !myCondition(car)).Count();

有用! 想想看。 第一个匹配项目的索引等于它之前的(不匹配)项目的数量。

讲故事的时间

我也不喜欢你在问题中已经提出的可怕的标准解决方案 。 就像我接受的答案一样,我去了一个普通的旧环路,虽然稍作修改:

public static int FindIndex<T>(this IEnumerable<T> items, Predicate<T> predicate) {
    int index = 0;
    foreach (var item in items) {
        if (predicate(item)) break;
        index++;
    }
    return index;
}

请注意,如果不匹配,它将返回项目数量而不是-1 。 但是现在让我们忽略这个小小的烦恼吧。 事实上, 可怕的标准解决方案在这种情况下崩溃, 我认为返回一个超越界限的索引

现在发生的事情是ReSharper告诉我Loop可以转换成LINQ表达式 。 虽然大多数时候这个功能会使可读性恶化,但这次结果令人惊叹。 所以荣誉JetBrains。

分析

优点

  • 简洁
  • 与其他LINQ组合
  • 避免new匿名对象
  • 只有在谓词首次匹配时才评估可枚举

因此,我认为它在时间和空间上是最佳的,同时保持可读性

缺点

  • 起初不是很明显
  • 没有匹配时不返回-1

当然,你总是可以将它隐藏在扩展方法之后。 如果没有匹配,最好做什么取决于上下文。




这是我刚刚放在一起的一个小扩展。

public static class PositionsExtension
{
    public static Int32 Position<TSource>(this IEnumerable<TSource> source,
                                          Func<TSource, bool> predicate)
    {
        return Positions<TSource>(source, predicate).FirstOrDefault();
    }
    public static IEnumerable<Int32> Positions<TSource>(this IEnumerable<TSource> source, 
                                                        Func<TSource, bool> predicate)
    {
        if (typeof(TSource) is IDictionary)
        {
            throw new Exception("Dictionaries aren't supported");
        }

        if (source == null)
        {
            throw new ArgumentOutOfRangeException("source is null");
        }
        if (predicate == null)
        {
            throw new ArgumentOutOfRangeException("predicate is null");
        }
        var found = source.Where(predicate).First();
        var query = source.Select((item, index) => new
            {
                Found = ReferenceEquals(item, found),
                Index = index

            }).Where( it => it.Found).Select( it => it.Index);
        return query;
    }
}

然后你可以这样称呼它。

IEnumerable<Int32> indicesWhereConditionIsMet = 
      ListItems.Positions(item => item == this);

Int32 firstWelcomeMessage ListItems.Position(msg =>               
      msg.WelcomeMessage.Contains("Hello"));