Getting the index of a sequence of items

2019-09-12 07:42发布

问题:

I was trying to get the index of a sequence of items inside an IEnumerable<T>

var collection = new[] { 1, 2, 3, 4, 5 };
var sequence   = new[] { 2, 3 };

// IndexOf is an extension method.
collection.IndexOf(sequence); // Should return 1

I wrote an IndexOf extension method for this and it works fine unless there are more than one of the first item of the sequence in collection, consecutively:

// There are two items that are 2, consecutively in the collection,
// which is the first item of the sequence.
var collection = new[] { 1, 2, 2, 3, 4, 5 };
var sequence   = new[] { 2, 3 };

collection.IndexOf(sequence); // Should return 2 but returns -1

Here is the IndexOf method:

public static int IndexOf<T>(this IEnumerable<T> collection,
    IEnumerable<T> sequence)
{
    var comparer = EqualityComparer<T>.Default;
    var counter = 0;
    var index = 0;
    var seqEnumerator = sequence.GetEnumerator();

    foreach (var item in collection)
        if (seqEnumerator.MoveNext())
        {
            if (!comparer.Equals(item, seqEnumerator.Current))
            {
                seqEnumerator.Dispose();
                seqEnumerator = sequence.GetEnumerator();
                counter = 0;

                // UPDATED AFTER MICHAEL'S ANSWER,
                // IT WORKS WITH THIS ADDED PART:
                seqEnumerator.MoveNext();
                if (comparer.Equals(item, seqEnumerator.Current))
                    counter++;
            }
            else counter++;
            index++;
        }
        else break;

    var done = !seqEnumerator.MoveNext();
    seqEnumerator.Dispose();
    return done ? index - counter : -1;
}

I couldn't figure out how to fix this.

回答1:

When you encounter wrong symbol on not first position you restarting the sequence iterator but you don't check if the current item is matching the start of the sequence iterator, so you actually never compare second 2 from collection to 2 from sequence .



回答2:

public static int IndexOf<T>(this IEnumerable<T> collection,
                                IEnumerable<T> sequence)
{
    var ccount = collection.Count();
    var scount = sequence.Count();

    if (scount > ccount) return -1;

    if (collection.Take(scount).SequenceEqual(sequence)) return 0;

    int index = Enumerable.Range(1, ccount - scount + 1)
                          .FirstOrDefault(i => collection.Skip(i).Take(scount).SequenceEqual(sequence));
    if (index == 0) return -1;
    return index;
}