views:

81

answers:

3

I've got two sequences:

IEnumerable<string> x = new[] { "a", "b", "c" };
IEnumerable<string> y = new[] { "a", "b", "d", "e" };

I'd like to find the common prefix of these two sequences (i.e. "a", "b"). Is there a succinct way to do this in LINQ?

Bear in mind that these aren't really IEnumerable<string>; they're IEnumerable<PathComponent>, where I have an implementation of IEqualityComparer<PathComponent>.

A: 

Maybe you could do something like this (not tested):

x.TakeWhile((item, index) => y.Contains(item) && y.IndexOf(item) == index);

EDIT. IndexOf is available only on arrays, so you should convert y to an array first. Also, you need to modify the code to use your IEqualityComparer.

Konamiman
+1  A: 

This works, but I don't know about the performance of the Skip() on the second array:

x.TakeWhile((s, i) => y.Skip(i).FirstOrDefault() == s);

Edit: Oops - here's a slightly better approach:

x.TakeWhile((s, i) => y.ElementAt(i) == s);
Matt Hamilton
@Matt: Tried that; works except that you need to ensure that 'y' is the longer of the two sequences...
Roger Lipscombe
+2  A: 

Dykam's mention of Zip led me to this:

public static class EnumerableExtensions
{
    public delegate bool EqualityComparison<in T1, in T2>(T1 x, T2 y);
    public delegate bool EqualityComparison<in T>(T x, T y);

    public static IEnumerable<T> CommonPrefix<T>(
        this IEnumerable<T> xs,
        IEnumerable<T> ys)
    {
        return CommonPrefix(xs, ys, EqualityComparer<T>.Default.Equals);
    }

    public static IEnumerable<T> CommonPrefix<T>(
        this IEnumerable<T> xs,
        IEnumerable<T> ys,
        EqualityComparison<T1, T2> eq)
    {
        IEnumerator<T> x = xs.GetEnumerator();
        IEnumerator<T> y = ys.GetEnumerator();

        while (x.MoveNext() && y.MoveNext() && eq(x.Current, y.Current))
        {
            yield return x.Current;
        }
    }

    public static IEnumerable<TResult> CommonPrefix<T1, T2, TResult> (
        this IEnumerable<T1> xs,
        IEnumerable<T2> ys,
        EqualityComparison<T1, T2> eq,
        Func<T1, T2, TResult> selector)
    {
        IEnumerator<T1> x = xs.GetEnumerator();
        IEnumerator<T2> y = ys.GetEnumerator();

        while (x.MoveNext() && y.MoveNext() && eq(x.Current, y.Current))
        {
            yield return selector(x.Current, y.Current);
        }
    }
}
Roger Lipscombe
Heh, made imperative, put in a Extension method. Should have done that myself. I actually made ZipSelect for own use in some Rest lib.
Dykam