使用 Linq 从列表中获取所有匹配值的索引

Posted

技术标签:

【中文标题】使用 Linq 从列表中获取所有匹配值的索引【英文标题】:Get indexes of all matching values from list using Linq 【发布时间】:2012-10-14 20:35:02 【问题描述】:

嘿,Linq 专家,

我刚刚问了一个非常相似的问题,并且知道解决方案可能非常简单,但我仍然无法理解如何使用 linq 以最有效的方式完成这项相当简单的任务。

我的基本情况是我有一个值列表,例如:

Lst1:
a
a
b
b
c
b
a
c
a

我想创建一个新列表,其中包含 Lst1 中的所有索引,例如 value = "a"。 所以,在这个例子中,我们会:

LstIndexes:
0
1
6
8

现在,我知道我可以使用 Loops 来做到这一点(我宁愿避免使用 Linq),我什至想出了如何通过以下方式使用 Linq 来做到这一点:

LstIndexes= Lst1.Select(Function(item As String, index As Integer) index) _
                .Where(Function(index As Integer) Lst1(index) = "a").ToList

我对此的挑战是它遍历列表两次,因此效率低下。

如何使用 Linq 以最有效的方式获得结果?

谢谢!!!!

【问题讨论】:

它在哪里迭代列表两次? 我想更好的问题是——你为什么认为它会遍历列表两次? 【参考方案1】:

首先,您的代码实际上并没有对列表进行两次迭代,而只是对其进行了一次迭代。

也就是说,您的 Select 实际上只是获取所有索引的序列;使用Enumerable.Range 更容易做到这一点:

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

理解为什么列表实际上没有迭代两次需要一些时间来适应。我会尽量给出一个基本的解释。

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

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

请注意,虽然这对您来说一开始看起来很复杂,但实际上计算机可以轻松完成所有这些工作。它实际上并不像最初看起来那样需要大量性能。

【讨论】:

Servy,感谢您添加该解释 - 这确实帮助我更多地理解事情!!!真的,非常感谢你!!! @JohnBus​​tos 郑重声明,我的代码执行时间不会比你的多或少,它只会让读者更清楚,并且在屏幕上占用的代码更少。 其实@Servy, Enumerable.Range(0, lst1.Count) 是合适的,因为 Range 会自动排除最高索引。您编写的代码将错过其输出中的“8”。 警告 - 我对 Enumerable.Range 为何如此行事的解释并不完全正确,但我认为没有人注意到 :) +1 因为与其他两个答案相比,它更容易阅读和理解查询的作用【参考方案2】:

这行得通,但可以说没有那么整洁。

var result = list1.Select((x, i) => new x, i)
                  .Where(x => x.x == "a")
                  .Select(x => x.i);

【讨论】:

【参考方案3】:

这个怎么样,对我来说很好用。

   static void Main(string[] args)
    
        List<char> Lst1 = new List<char>();
        Lst1.Add('a'); 
        Lst1.Add('a');   
        Lst1.Add('b');   
        Lst1.Add('b');   
        Lst1.Add('c');   
        Lst1.Add('b');   
        Lst1.Add('a');   
        Lst1.Add('c');
        Lst1.Add('a');

        var result = Lst1.Select((c, i) => new  character = c, index = i )
                         .Where(list => list.character == 'a')
                         .ToList();
    

【讨论】:

这将产生一个包含索引和字符的匿名类型列表;他只需要索引。 @Servy,呵呵,...Select(o =&gt; o.index); :) @2kay 或者您可以只使用我提出的解决方案,这样您就不会执行两个选择然后撤消映射。

以上是关于使用 Linq 从列表中获取所有匹配值的索引的主要内容,如果未能解决你的问题,请参考以下文章

使用Linq获取列表中对象的索引[重复]

如何获取numpy数组中所有NaN值的索引列表?

使用 LINQ 获取列表的一部分 [重复]

比较python中的两个列表并返回匹配值的索引

使用 LINQ 排序后获取集合中项目的新索引

获取回答熊猫过滤器的所有值的索引