StartWith method for arrays

时间:2016-10-20 19:54:26

标签: c# .net performance linq

Is there a StartWith method for arrays in .NET? Or something similar to it in LINQ?

var arr1 = { "A", "B, "C" }
var arr2 = { "A", "B, "C", "D" } 

var arr3 = { "A", "B, "CD" } 
var arr4 = { "E", "A, "B", "C" } 

arr2.StartWith(arr1) // true
arr1.StartWith(arr2) // false

arr3.StartWith(arr1) // false
arr4.StartWith(arr1) // false

Or I should do it straightforward:

bool StartWith(string[] arr1, string[] arr2)
{
     if (arr1.Count() < arr2.Count) return false;

     for (var i = 0; i < arr2.Count(), i++)
     {
        if (arr2[i] != arr1[i]) return false;
     }

     return true;
}

I'm looking for the most efficient way to do that.

6 个答案:

答案 0 :(得分:6)

bool answer = arr2.Take(arr1.Length).SequenceEqual(arr1);

答案 1 :(得分:6)

Your "striaghtformward" way is the way most LINQ methods would be doing it anyway. There are a few tweaks you could do. For example make it a extension method and use a comparer for the comparison of the two types so custom comparers could be used.

public static class ExtensionMethods
{
    static bool StartWith<T>(this T[] arr1, T[] arr2)
    {
        return StartWith(arr1, arr2, EqualityComparer<T>.Default);
    }

    static bool StartWith<T>(this T[] arr1, T[] arr2, IEqualityComparer<T> comparer)
    {
         if (arr1.Length < arr2.Length) return false;

         for (var i = 0; i < arr2.Length, i++)
         {
            if (!comparer.Equals(arr2[i], arr1[i])) return false;
         }

         return true;
    }
}

UPDATE: For fun I decided to take the time and write a little more "advanced" version that would work with any IEnumerable<T> and not just arrays.

public static class ExtensionMethods
{
    static bool StartsWith<T>(this IEnumerable<T> @this, IEnumerable<T> @startsWith)
    {
        return StartsWith(@this, startsWith, EqualityComparer<T>.Default);
    }

    static bool StartsWith<T>(this IEnumerable<T> @this, IEnumerable<T> startsWith, IEqualityComparer<T> comparer)
    {
        if (@this == null) throw new ArgumentNullException("this");
        if (startsWith == null) throw new ArgumentNullException("startsWith");
        if (comparer == null) throw new ArgumentNullException("comparer");

        //Check to see if both types implement ICollection<T> to get a free Count check.
        var thisCollection = @this as ICollection<T>;
        var startsWithCollection = startsWith as ICollection<T>;
        if (thisCollection != null && startsWithCollection != null && (thisCollection.Count < startsWithCollection.Count)) 
            return false;

        using (var thisEnumerator = @this.GetEnumerator())
        using (var startsWithEnumerator = startsWith.GetEnumerator())
        {
            //Keep looping till the startsWithEnumerator runs out of items.
            while (startsWithEnumerator.MoveNext())
            {
                //Check to see if the thisEnumerator ran out of items.
                if (!thisEnumerator.MoveNext())
                    return false;

                if (!comparer.Equals(thisEnumerator.Current, startsWithEnumerator.Current))
                    return false;

            }
        }
        return true;
    }
}

答案 2 :(得分:5)

You can do:

var result = arr2.Take(arr1.Length).SequenceEqual(arr1);

To optimize it further you can add the check arr2.Length >= arr1.Length in the start like:

var result = arr2.Length >= arr1.Length && arr2.Take(arr1.Length).SequenceEqual(arr1);

The end result would be same.

答案 3 :(得分:3)

Try Enumerable.SequenceEqual(a1, a2) but trim your first array, i.e.,

 var arr1 = { "A", "B, "C" }
 var arr2 = { "A", "B, "C", "D" }

 if (Enumerable.SequenceEqual(arr1, arr2.Take(arr1.Length))

答案 4 :(得分:0)

You don't want to require everything to be an array, and you don't want to call Count() on an IEnumerable<T> that may be a large query, when you only really want to sniff at the first four items or whatever.

public static class Extensions
{
    public static void Test()
    {
        var a = new[] { "a", "b" };
        var b = new[] { "a", "b", "c" };
        var c = new[] { "a", "b", "c", "d" };
        var d = new[] { "x", "y" };

        Console.WriteLine("b.StartsWith(a): {0}", b.StartsWith(a));
        Console.WriteLine("b.StartsWith(c): {0}", b.StartsWith(c));
        Console.WriteLine("b.StartsWith(d, x => x.Length): {0}", 
                          b.StartsWith(d, x => x.Length));
    }

    public static bool StartsWith<T>(
        this IEnumerable<T> sequence, 
        IEnumerable<T> prefixCandidate, 
        Func<T, T, bool> compare = null)
    {
        using (var eseq = sequence.GetEnumerator())
        using (var eprefix = prefixCandidate.GetEnumerator())
        {

            if (compare == null)
            {
                compare = (x, y) => Object.Equals(x, y);
            }

            eseq.MoveNext();
            eprefix.MoveNext();

            do
            {
                if (!compare(eseq.Current, eprefix.Current))
                    return false;

                if (!eprefix.MoveNext())
                    return true;
            }
            while (eseq.MoveNext());

            return false;
        }
    }

    public static bool StartsWith<T, TProperty>(
        this IEnumerable<T> sequence,
        IEnumerable<T> prefixCandidate,
        Func<T, TProperty> selector)
    {
        using (var eseq = sequence.GetEnumerator())
        using (var eprefix = prefixCandidate.GetEnumerator())
        {
            eseq.MoveNext();
            eprefix.MoveNext();

            do
            {
                if (!Object.Equals(
                        selector(eseq.Current),
                        selector(eprefix.Current)))
                {
                    return false;
                }

                if (!eprefix.MoveNext())
                    return true;
            }
            while (eseq.MoveNext());

            return false;
        }
    }
}

答案 5 :(得分:0)

以下是一些不同的方法。我没有优化或完全验证一切,各地都有改进的余地。但这应该会给你一些想法。

最佳性能将始终处于低水平,如果你抓住迭代器并逐步进行,你可以获得更快的结果。

方法和绩效结果:

StartsWith1   00:00:01.9014586
StartsWith2   00:00:02.1227468
StartsWith3   00:00:03.2222109
StartsWith4   00:00:05.5544177

测试方法:

var watch = new Stopwatch();
watch.Start();
for (int i = 0; i < 10000000; i++)
{
    bool test = action(arr2, arr1);
}
watch.Stop();
return watch.Elapsed;

方法:

public static class IEnumerableExtender
{
    public static bool StartsWith1<T>(this IEnumerable<T> source, IEnumerable<T> compare)
    {
        if (source.Count() < compare.Count())
        {
            return false;
        }

        using (var se = source.GetEnumerator())
        {
            using (var ce = compare.GetEnumerator())
            {
                while (ce.MoveNext() && se.MoveNext())
                {
                    if (!ce.Current.Equals(se.Current))
                    {
                        return false;
                    }
                }
            }
        }

        return true;
    }

    public static bool StartsWith2<T>(this IEnumerable<T> source, IEnumerable<T> compare) =>
        compare.Take(source.Count()).SequenceEqual(source);

    public static bool StartsWith3<T>(this IEnumerable<T> source, IEnumerable<T> compare)
    {
        if (source == null)
        {
            throw new ArgumentNullException(nameof(source));
        }

        if (compare == null)
        {
            throw new ArgumentNullException(nameof(compare));
        }

        if (source.Count() < compare.Count())
        {
            return false;
        }

        return compare.SequenceEqual(source.Take(compare.Count()));
    }

    public static bool StartsWith4<T>(this IEnumerable<T> arr1, IEnumerable<T> arr2)
    {
        return StartsWith4(arr1, arr2, EqualityComparer<T>.Default);
    }
    public static bool StartsWith4<T>(this IEnumerable<T> arr1, IEnumerable<T> arr2, IEqualityComparer<T> comparer)
    {
        if (arr1.Count() < arr2.Count()) return false;

        for (var i = 0; i < arr2.Count(); i++)
        {
            if (!comparer.Equals(arr2.ElementAt(i), arr1.ElementAt(i))) return false;
        }

        return true;
    }
}