[英]How to find a sub-list of two consecutive items in a list using Linq
假設我們有一個字符串列表,例如
"A", "B", "C", "D", "E", "F"
現在,我想在此列表中搜索兩個連續項目DE
的子列表。 如何使用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);
這是正確的解決方案嗎? 有沒有更短的解決方案?
您可以按照以下方式重寫方法:
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
里面的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");
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.