简体   繁体   中英

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

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 DE 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?

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)) {
        ...
    }
}

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, eg {"E", "C", "D", "E"}

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).

最优雅的解决方案是最简单的

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

这不是一个很好的一般或有效的答案,而是简洁的-因为它是字符串/字符的列表,所以您可以执行一个简单的string.Join()并检查子字符串:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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