views:

73

answers:

3

Is there a way of searching a list for more that one consecutive values? I've looking at Find and IndexOf but Find uses Predicates that only use the current value and IndexOf only takes byte parameters.

I can code my own solution but I want to be sure that there isn't a solution already available to this common problem.

Thanks in advance.

A: 

I don't think it's a particularly common problem, to be honest - I'm pretty sure there's nothing in the framework.

I suspect you'll need to work out whether you want efficiency or simplicity to implement your own. A fairly simple extension method version might look like this:

public static int IndexOf<T>(this IEnumerable<T> source,
                             T first,
                             T second)
{
    IEqualityComparer<T> comparer = EqualityComparer<T>.Default;

    // We can only return when we've read source[index+1], so we need
    // to keep one value behind
    int index=-1;
    T prev = default(T);
    foreach (T element in source)
    {
        if (comparer.Equals(first, prev) &&
            comparer.Equals(second, element) &&
            index >= 0) // Avoid edge cases where first=default(T)
        {
            return index;
        }
        index++;
        prev = element;
    }
    return -1;
}
Jon Skeet
It's just that as we can search a list or array for one item looks like common to search for more than one item. Anyway thanks for your response, I've coded a generic solution that searches for n items.
+1  A: 

One idea might be to split your list into groups of consecutive values and then compare the contents of each. Here's another function (lifted from the F# core library) which will perform the split.

static IEnumerable<T[]> Windowed<T>(this IEnumerable<T> source, int size)
{
  if (source == null)
    throw new NullReferenceException();
  if (size <= 0)
    throw new ArgumentOutOfRangeException("size", "The window size must be positive.");

  var arr = new T[size];
  var r = size-1;
  var i = 0;
  foreach (T item in source)
  {
    arr[i] = item;
    i = (i+1) % size;
    if (r == 0)
    {
      var res = new T[size];
      for (int j = 0; j < size; j++)
        res[j] = arr[(i+j) % size];
      yield return res;
    }
    else 
      r -= 1;
  }
}

You could use the above function like so:

var result = Enumerable.Range(1, 10).Windowed(2)
               .Where(a => a[0] == 3 && a[1] == 4)
               .First();
Dustin Campbell
A: 

This is my own solution to the question. I like the method because it can handle any amount of items, is O(n) and its pretty simple:

    <Extension()> Public Function Find(Of T)(ByVal list As List(Of T), ByVal searchedValue As T(), ByVal startingIndex As Integer) As Integer
    Dim mainIndex As Integer
    Dim searchedIndex As Integer
    Dim result As Integer

    result = -1 ' Initialize result
    mainIndex = startingIndex

    While mainIndex < list.Count AndAlso result = -1
        If list(mainIndex).Equals(searchedValue(0)) Then
            searchedIndex = 0
            While searchedIndex < searchedValue.Length AndAlso (list(mainIndex + searchedIndex).Equals(searchedValue(searchedIndex)))
                searchedIndex = searchedIndex + 1
            End While

            If searchedIndex = searchedValue.Length AndAlso list(mainIndex + searchedIndex - 1).Equals(searchedValue(searchedIndex - 1)) Then
                result = mainIndex
            End If
        End If

        mainIndex = mainIndex + 1
    End While

    Return result
End Function
It's not O(n). It's O(n * m) where m is the size of the search value. If you need to look for XXXXXXXXXXXXXXXXXY in a string which is just XXXXXXXXXXXXXXXXXXXXX etc, then you'll do a lot of unnecessary comparisons.
Jon Skeet
I'd also recommend returning directly from the loop when you've found a match. I've never liked the mantra of making code more complicated just to make sure there's only a single point of return.
Jon Skeet
Ok, I agree with your first comment, its O(n*m). On the other side I love the mantra of only one returning point and I use it always. To me its more complicated to look how many returning points there are inside a function.