繁体   English   中英

LINQ查找值的数组索引

[英]LINQ to find array indexes of a value

假设我有以下字符串数组:

string[] str = new string[] {"max", "min", "avg", "max", "avg", "min"}

是否可以使用LINQ获取与一个字符串匹配的索引列表?

作为一个例子,我想搜索字符串“avg”并获得一个包含的列表

2,4

意思是“avg”可以在str [2]和str [4]中找到。

.Select有一个很少使用的重载,它产生一个索引。 你可以像这样使用它:

str.Select((s, i) => new {i, s})
    .Where(t => t.s == "avg")
    .Select(t => t.i)
    .ToList()

结果将是包含2和4的列表。

文档在这里

你可以这样做:

str.Select((v,i) => new {Index = i, Value = v}) // Pair up values and indexes
   .Where(p => p.Value == "avg") // Do the filtering
   .Select(p => p.Index); // Keep the index and drop the value

关键步骤是使用Select的重载,它将当前索引提供给您的仿函数。

您可以使用传递索引的Enumerable.Select的重载,然后在匿名类型上使用Enumerable.Where

List<int> result = str.Select((s, index) => new { s, index })
                      .Where(x => x.s== "avg")
                      .Select(x => x.index)
                      .ToList();

如果你只想找到第一个/最后一个索引,你还有内置方法List.IndexOfList.LastIndexOf

int firstIndex = str.IndexOf("avg");
int lastIndex = str.LastIndexOf("avg");

(或者你可以使用这个带有起始索引来指定起始位置的重载

虽然你可以使用SelectWhere的组合,但这可能是制作你自己的函数的一个很好的Select

public static IEnumerable<int> Indexes<T>(IEnumerable<T> source, T itemToFind)
{
    if (source == null)
        throw new ArgumentNullException("source");

    int i = 0;
    foreach (T item in source)
    {
        if (object.Equals(itemToFind, item))
        {
            yield return i;
        }

        i++;
    }
}

首先,您的代码实际上不会在列表上迭代两次,它只迭代一次。

也就是说,你的Select实际上只是得到了所有索引的序列; 使用Enumerable.Range更容易完成:

 var result = Enumerable.Range(0, str.Count)
                 .Where(i => str[i] == "avg")
                 .ToList();

理解为什么列表实际上没有被迭代两次将需要一些时间来习惯。 我会试着给出一个基本的解释。

您应该考虑大多数LINQ方法,例如Select和Where作为管道。 每种方法都做了一些工作。 在Select的情况下,你给它一个方法,它基本上说,“每当有人问我下一个项目时,我会首先询问我的输入序列的项目,然后使用我必须的方法将其转换为其他东西,然后把那个项目交给任何使用我的人。“ 或多或少地说,“每当有人问我一个项目时,我会问我的输入序列是否有项目,如果函数说它很好我会传递它,如果不是,我会继续要求项目直到我得到一个通过。“

所以当你链接他们时会发生的事情是ToList请求第一个项目,它会转到Where它为它的第一个项目,Where转到Select并询问它的第一个项目,Select转到列表以询问它的第一个项目项目。 然后该列表提供了它的第一个项目。 选择然后将该项转换为需要吐出的内容(在本例中,只是int 0)并将其转移到Where。 在哪里获取该项并运行它的函数,该函数确定它是真的,因此吐出0到ToList,将其添加到列表中。 那整件事情又发生了9次。 这意味着Select将最终要求列表中的每个项目恰好一次,并将其每个结果直接提供给Where,这将把“通过测试”的结果直接提供给ToList,ToList将它们存储在列表中。 所有LINQ方法都经过精心设计,只能迭代源序列一次(当它们被迭代一次时)。

请注意,虽然这对您来说似乎很复杂,但计算机实际上很容易完成所有这些操作。 它实际上并不像最初看起来那样具有性能密集程度。

你需要一个组合的select和where运算符,与接受的答案相比,这将更便宜,因为不需要中间对象:

public static IEnumerable<TResult> SelectWhere<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, bool> filter, Func<TSource, int, TResult> selector)
        {
            int index = -1;
            foreach (var s in source)
            {
                checked{ ++index; }
                if (filter(s))
                    yield return selector(s, index);
            }
        }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM