LINQ 中的隐式索引或生成索引

Implicit Index or Generating Index in LINQ

我最近遇到几个案例,这让我想知道是否有任何方法可以获取内部索引,或者如果没有,可以在使用 LINQ 时有效地生成索引。

考虑以下情况:

List<int> list = new List() { 7, 4, 5, 1, 7, 8 };

在 C# 中,如果我们要使用 LINQ return 上述数组中“7”的索引,我们不能简单地使用 IndexOf/LastIndexOf - 因为它只会 return first/last 结果。

这行不通:

var result = list.Where(x => x == 7).Select(y => list.IndexOf(y));

但是,我们有几种解决方法。

例如,一种方法可能是这样(我通常这样做):

int index = 0;
var result = from x in list
        let indexNow = index++
        where x == 7
        select indexNow;

使用let会引入额外的参数indexNow。

还有另一种方式:

var result = Enumerable.Range(0, list.Count).Where(x => list.ElementAt(x) == 7);

它将使用Enumerable.Range生成另一组项目并从中选择结果。

现在,我想知道是否有更简单的替代方法,例如:

  1. 如果有任何(内置)方法可以获取 IEnumerable 的内部索引而无需使用 let 或
  2. 声明某些内容
  3. 使用 Enumerable.Range 以外的东西为 IEnumerable 生成索引(也许像新的东西?我不太熟悉如何去做),或者
  4. 任何其他可以缩短代码但仍然获得 IEnumerable 索引的东西。

来自 IEnumerable.Select with index, How to get index using LINQ?, and so on: IEnumerable<T>.Select() has an overload that provides an index

使用它,您可以:

  1. 投射到包含索引和值的匿名类型(或者 Tuple<int, T> 如果您想将其公开给其他方法,匿名类型是本地的)。
  2. 根据您要查找的值过滤这些结果。
  3. 仅投影索引。

因此代码将如下所示:

var result = list.Select((v, i) => new { Index = i, Value = v })
                 .Where(i => i.Value == 7)
                 .Select(i => i.Index);

现在 result 将包含一个包含所有索引的枚举。

请注意,这仅适用于保证插入顺序并提供数字索引器的源集合类型,例如 List<T>。当您将此用于 Dictionary<TKey, TValue>HashSet<T> 时,生成的索引将无法用于索引到字典中。