假设我们有一个像
这样的字符串列表"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);
这是一个正确的解决方案吗?有没有更短的解决方案?
答案 0 :(得分:3)
您可以按如下方式重写您的方法:
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)) {
...
}
}
答案 1 :(得分:1)
我不认为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"}
旁边,则仍会匹配,例如{{1}}
答案 2 :(得分:1)
我没有看到比你更短的解决方案。但我认为你建议的解决方案只有在第一个字符串没有在列表中出现两次时才有效。 例如,如果列表是:
"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。限制是如果子列表出现多次,您可以获得其中任何一个的索引(不一定是第一个)。
答案 3 :(得分:1)
最优雅的解决方案是最简单的
Where((x,i) => (x == "D") && (i != list.Count - 1) && (list[i + 1] == "E")).FirstOrDefault();
&#13;
答案 4 :(得分:0)
不是一般的或有效的答案,但是简洁 - 因为它是一个字符串/字符列表,你可以做一个简单的string.Join()
并检查子字符串:
int p = string.Join("", list).IndexOf("DE");