How to find a sub-list of two consecutive items in a list using Linq

Deadly 提交于 2021-02-05 08:15:46

问题


Suppose that we have a list of strings like

"A", "B", "C", "D", "E", "F"

Now I'd like to search for a sub-list of two consecutive items D E in this list. How can I do this using Linq?

my approach is:

int i = list.FindIndex(x => x == "D");
int j = list.FindIndex(x => x == "E");

int p = i < 0 || j < 0 ? -1 : (j == i + 1 ? i : -1);

Is it a correct solution? Is there any shorter solution?


回答1:


You could rewrite your approach as follows:

bool hasSublist = list
    .SkipWhile(x => x != "D")
    .Take(2)
    .SequenceEquals(new[] {"D", "E"});

If you need the starting index of {"D", "E"}, you could add a select that pairs up letters and their indexes.

However, the problem with your approach is that it would miss the subsequence if there's another "D" not followed by an "E", for example

"D" "A" "D" "B" "D" "C" "D" "D" "D" "E"

There is a "D" "E" at the end, but your approach stops after finding the first "D".

If you are looking for sublists of length 2, you could use Zip, like this:

bool hasSublist = list
    .Zip(list.Skip(1), (a, b) => new {a, b})
    .Any(p => p.a == "D" && p.b == "E");

However, this does not scale for longer sub-lists.

Using a plain for loop would work much better:

for (var i = 0 ; i < list.Count-1 ; i++) {
    if (list[i] == "D" && list[i+1] == "E") {
        ...
    }
}

The if inside could be replaced with SequenceEquals, accommodating sub-lists of any length:

var desiredSublist = new[] {"D", "E", "F"};
for (var i = 0 ; i < list.Count-desiredSublist+1 ; i++) {
    if (list.Skip(i).SequenceEquals(desiredSublist)) {
        ...
    }
}



回答2:


I don't think LINQ is appropriate here.

A more efficient solution could be to find "D" then just check it isn't at the end and that "E" is at the next index:

int i = list.FindIndex(x => x == "D");
int p = (i < list.Count - 1) && (list[i + 1] == "E") ? i : -1;

This avoids looping twice to find both indices, and also still matches if "E" appears next to "D" but also before it, e.g. {"E", "C", "D", "E"}




回答3:


I didn't see a shorter solution than yours. But I think the solution you propose only works if first string doesn't appear twice in the list. For example, if the list was:

"A", "D", "B", "C", "D", "E", "F"

I think your proposal will not work because first FindIndex will return the index of the first "D", which is not followed by "E".

A posible alternative could be (should be tested to be sure):

    int index=-1;
    Parallel.For(0, list.Count - 1, i =>
    {
      if (list[i] == "D" && list[i + 1] == "E")
      {
          Interlocked.Exchange(ref index, i);
      }
    }); 
//after the loop, if index!=-1, sublist was found and starts at index position

Not shorter, of course, but can be faster if list is very large, because using Parallel.For. A limitation is that if the sublist appears several times, you can obtain the index of any of them (not necessarily the first one).




回答4:


The most elegant solution is the simplest

Where((x,i) => (x == "D") && (i != list.Count - 1) && (list[i + 1] == "E")).FirstOrDefault();



回答5:


Not a good general or efficient answer, but concise - as it is a list of strings / characters, you could do a simple string.Join() and check for the substring:

int p = string.Join("", list).IndexOf("DE");


来源:https://stackoverflow.com/questions/35379419/how-to-find-a-sub-list-of-two-consecutive-items-in-a-list-using-linq

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!