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


嘿林克专家在那里,

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

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

Lst1:
a
a
b
b
c
b
a
c
a

我想创建一个新列表,它将保存Lst1中的所有索引,例如,值=“a”。 所以,在这个例子中,我们将:

LstIndexes:
0
1
6
8

现在,我知道我可以用循环来做这个(我宁愿避免使用Linq),我甚至想出了如何用Linq以下面的方式做到这一点:

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

我对此的挑战是它在列表上迭代两次因此效率低下。

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

谢谢!!!!


22
2017-10-24 18:25


起源


它在列表中迭代两次? - Austin Salonen
我想更好的问题是 - 为什么你认为它在列表上迭代两次? - Austin Salonen


答案:


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

那说,你的 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,ToList将它们存储在列表中。所有LINQ方法都经过精心设计,只能迭代源序列一次(当它们被迭代一次时)。

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


52
2017-10-24 18:31



Servy,谢谢你加入那个解释 - 这确实帮助我理解了更多的东西!真的,非常感谢你! - John Bustos
@JohnBus​​tos为了记录我的代码实际上不会花费比你更多或更少的时间执行,它只会让读者更清楚,并且在屏幕上占用更少的代码。 - Servy
实际上@Servy,Enumerable.Range(0,lst1.Count)是正确的,因为Range自动排除最高索引。您编写的代码将从输出中错过“8”。 - Dean Radcliffe
@DeanRadcliffe是的,你是对的。刚刚验证过。 - Servy
+1因为它比其他两个答案更容易阅读和理解查询的作用 - StupidOne


这有效,但可以说不是很整洁。

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

7
2017-10-24 19:14





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

   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();
    }

1
2017-10-24 18:47



这将导致包含索引和字符的匿名类型列表;他只需要索引。 - Servy
@Servy,呵呵, ...Select(o => o.index); :) - 2kay
是的,你是对的,但也许对他有用:P - luis_laurent
@ 2kay或者您可以使用我提出的解决方案,这样您就没有两个选择执行然后撤消映射。 - Servy