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.