How to get position of a sub-list within another list?

Asked

Viewed 1,311 times

4

I have a list of bytes and I need to check if it contains a sub-list of bytes in a specific order.

How can I do this check by getting the position of the sub-list in a simple way, using the resources of the Linq or the List, that is, without the need to make a for checking position to position.

Exemplifying:

List<byte> bytes = new List<byte> { 10, 20, 30, 40, 50, 60, 70, 80  };
List<byte> pattern = new List<byte> { 30, 40, 50 };

int indiceInicioSublista = ???

2 answers

2


You can nest two loops to do this: for each valid position of the main list, check whether each item of the list being searched beats. If you reach the end without finding, then returns a negative value, indicating that you did not find.

The LINQ version, equivalent is at the end of the answer.

public static int FindSubList<T>(IList<T> mainList, IList<T> subList)
{
    for (int it = 0; it <= mainList.Count - subList.Count; it++)
    {
        bool allEquals = true;
        for (int it2 = 0; it2 < subList.Count; it2++)
        {
            if (!mainList[it + it2].Equals(subList[it2]))
            {
                allEquals = false;
                break;
            }
        }

        if (allEquals)
            return it;
    }

    return -1;
}

Using the LINQ would look like this, which is the equivalent of the above code:

public static int FindSubList<T>(IList<T> mainList, IList<T> subList)
{
    for (int it = 0; it <= mainList.Count - subList.Count; it++)
        if (!subList.Where((t, it2) => !mainList[it + it2].Equals(t)).Any())
            return it;

    return -1;
}

Another alternative using LINQ on IEnumerable, however this solution is slower than above:

public static int FindSubEnumerable<T>(IEnumerable<T> mainList, IEnumerable<T> subList)
{
    return
        Enumerable.Range(1, mainList.Count())
            .FirstOrDefault(it => mainList.Skip(it - 1).Take(subList.Count()).SequenceEqual(subList)) - 1;
}
  • 2

    Thanks, but I’m looking for a simpler version without using for. Using for I’ve already managed to do.

  • I have indicated a LINQ solution! It is at the end of the answer.

  • Now I have indicated another alternative, using pure LINQ, about IEnumerable instead of lists.

  • The version with IEnumerable is slower, I recommend the second solution, which is using LINQ on IList.

  • @Miguelangelo in your code should not be <byte> instead of <int> ?

  • @FCCDIAS: Really! I made the codes generic so that any kind of data is supported.

  • The LINQ version with Ilist is still in trouble, I’ll fix it.

  • I tested the last option, and it worked correctly. Thank you.

  • Fixed, when converting to generic, inverti unintentionally the test condition.

Show 4 more comments

1

Takes the equality of elements independent of the position.

List<byte> result = bytes.Where(e => pattern.Contains(e)).ToList<byte>();
IEnumerable<byte> result1 = bytes.Intersect(pattern);
IList<byte> result3 = bytes.Intersect(pattern).ToList<Byte>();

Return true if bytes is inside pattern, that were converted to String.

bool retorno = String.Join("", bytes).Contains(string.Join("", pattern));

References

1 - Getting Started with LINQ in C#

2 - 101 LINQ Samples

  • 1

    That way, even if the Pattern changes, it keeps bringing the result. The order of the Pattern is important.

  • So, @Renatodinhaniconceição, in case it will bring the elements that repeats itself independent of the order !!! is this !!! or got it wrong

  • So, actually, order is important yes.

  • Pattern is a protocol header, and when it is found, all content after it has a different meaning.

  • Change the Pattern to { 30, 50, 40 }. It’s not to return anything.

  • Dear @Renatodin Iconceição, because, you did not provide this in the question, that the data has sequence ?

  • First line of the question: "if it contains a sub-list of bytes in a specific order."

  • @Renatodinhaniconceição this is not clear

Show 3 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.