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
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, e.g. {"E", "C", "D", "E"}
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)) {
...
}
}
The most elegant solution is the simplest
Where((x,i) => (x == "D") && (i != list.Count - 1) && (list[i + 1] == "E")).FirstOrDefault();
Not a good general or efficient answer, but concise - as it is a list of strings / characters, you could do a simple string.Join()
and check for the substring:
int p = string.Join("", list).IndexOf("DE");
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).