如何使用 Linq 在列表中查找包含两个连续项的子列表

本文关键字:列表 连续 包含两 查找 何使用 Linq | 更新日期: 2023-09-27 18:34:31

假设我们有一个字符串列表,例如

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

现在,我想搜索此列表中D E的两个连续项目的子列表。如何使用 Linq 执行此操作?

我的方法是:

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

这是一个正确的解决方案吗?有没有更短的解决方案?

如何使用 Linq 在列表中查找包含两个连续项的子列表

您可以按如下方式重写您的方法:

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

如果您需要 {"D", "E"} 的起始索引,您可以添加一个将字母及其索引配对的选择。

但是,您的方法的问题在于,如果有另一个"D"后没有"E",它将错过子序列,例如

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

最后有一个"D" "E",但你的方法在找到第一个"D"后停止了.

如果要查找长度为 2 的子列表,可以使用 Zip ,如下所示:

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

但是,这不适用于较长的子列表。

使用普通for循环会更好:

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

里面的if可以替换为SequenceEquals,容纳任何长度的子列表:

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

LINQ在这里不合适。

更有效的解决方案可能是找到"D"然后检查它不在末尾,"E"在下一个索引:

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

这样可以避免循环两次以查找两个索引,并且如果"E"出现在"D"旁边但也出现在索引之前,则仍然匹配,例如 {"E", "C", "D", "E"}

我没有看到比你的解决方案更短的解决方案。但我认为您提出的解决方案仅在第一个字符串在列表中没有出现两次时才有效。例如,如果列表为:

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

我认为您的建议将不起作用,因为第一个 FindIndex 将返回第一个"D"的索引,该索引后面不跟"E"。

一个可能的替代方案可能是(应该进行测试以确保(:

    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

当然,不是更短,但如果列表非常大,可以更快,因为使用 Parallel.For。限制是,如果子列表出现多次,则可以获取其中任何一个的索引(不一定是第一个(。

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

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

不是一个很好的通用或有效的答案,但简洁 - 因为它是字符串/字符列表,您可以做一个简单的string.Join()并检查子字符串:

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