Optimize LINQ search with custom fixed ranking I have to perform on some collection and return ranking based on some logic
I wish to optimize this working code since I think it can be bettered (maybe using tasks?).

I need to search on Counterparts item. I load this data from DB and I've got more aliases per counterpart.

I need to return the result based on those criteria:


Counterpart Code is equal to search string
Counterpart Code starts with search string
Counterpart Code contains search string
Counterpart Description contains search string
Counterpart Alias is equal to search string
Counterpart Alias starts with search string
Counterpart Alias contains search string


Each of those rules starts from Ranking 1 to 7 and I have to sort ok that ranking ascending.

public class Counterpart
{
    public int Id { get; set; }

    public string Code { get; set; }

    public string Description { get; set; }

    public IEnumerable&lt;Alias&gt; Aliases { get; set; }

    public override bool Equals(object obj)
    {
        Counterpart obj2 = obj as Counterpart;
        if (obj2 == null) return false;
        return Id == obj2.Id;
    }
}

public class Alias
{
    public int? Type { get; set; }

    public string Description { get; set; }
}

internal class CounterPartRanking
{
    public int Rank { get; set; }

    public Counterpart CounterPart { get; set; }
}

public static class CounterpartExtensions
{
    public static IEnumerable&lt;Counterpart&gt; SearchWithRank(this IEnumerable&lt;Counterpart&gt; source, string pattern)
    {
        var items1 = source.Where(x =&gt; x.Code == pattern);
        var items2 = source.Where(x =&gt; x.Code.StartsWith(pattern));
        var items3 = source.Where(x =&gt; x.Code.Contains(pattern));
        var items4 = source.Where(x =&gt; x.Description.Contains(pattern));
        var items5 = source.Where(x =&gt; x.Aliases != null &amp;&amp; x.Aliases.Any(y =&gt; y.Description == pattern));
        var items6 = source.Where(x =&gt; x.Aliases != null &amp;&amp; x.Aliases.Any(y =&gt; y.Description.StartsWith(pattern)));
        var items7 = source.Where(x =&gt; x.Aliases != null &amp;&amp; x.Aliases.Any(y =&gt; y.Description.Contains(pattern)));

        Stopwatch sw = Stopwatch.StartNew();
        var rankedItems = new List&lt;CounterPartRanking&gt;();


        if (items1.Any())
            rankedItems.AddRange(items1.Select(x =&gt; new CounterPartRanking { Rank = 1, CounterPart = x }));
        if (items2.Any())
            rankedItems.AddRange(items2.Select(x =&gt; new CounterPartRanking { Rank = 2, CounterPart = x }));
        if (items3.Any())
            rankedItems.AddRange(items3.Select(x =&gt; new CounterPartRanking { Rank = 3, CounterPart = x }));
        if (items4.Any())
            rankedItems.AddRange(items4.Select(x =&gt; new CounterPartRanking { Rank = 4, CounterPart = x }));
        if (items5.Any())
            rankedItems.AddRange(items5.Select(x =&gt; new CounterPartRanking { Rank = 5, CounterPart = x }));
        if (items6.Any())
            rankedItems.AddRange(items6.Select(x =&gt; new CounterPartRanking { Rank = 6, CounterPart = x }));
        if (items7.Any())
            rankedItems.AddRange(items7.Select(x =&gt; new CounterPartRanking { Rank = 7, CounterPart = x }));

        sw.Stop();

        Debug.WriteLine("Time elapsed {0} for {1}", sw.Elapsed, pattern);
        var items = rankedItems.OrderBy(x =&gt; x.Rank).Select(x =&gt; x.CounterPart);
        var distinct = items.Distinct();

        return distinct;
    }
}
Distinct

Take a look at the documentation for Distinct.

A couple things from the remarks should stand out for you:


  The Distinct(IEnumerable) method returns an unordered sequence that contains no duplicate values.


I know that the order of the sequence is preserved when using Distinct but that's an implementation detail - there's no gaurantee.


  The default equality comparer, Default, is used to compare values of the types that implement the IEquatable generic interface. To compare a custom data type, you must implement this interface and provide your own GetHashCode and Equals methods for the type.


Your custom type Counterpart does not do this. It should implement IEquatable&lt;Counterpart&gt; and additionally override both GetHashCode and Equals.

public class CounterPart : IEquatable&lt;Counterpart&gt;
{
    // ...
    public bool Equals(Counterpart other)
    {
        // left for you.
    } 

    public override bool Equals(object other)
    {
        return Equals(other as Counterpart);
    }

    public override bool GetHashCode()
    {
         // http://stackoverflow.com/a/263416/1402923
    }
}


Now you have a type that is safe for use with Distinct!

AddRange

As far as I know, AddRange only throws when the collection is null. You don't need to check for items before you call it. That elminates a whole heap of code:

rankedItems.AddRange(items1.Select(x =&gt; new CounterPartRanking { Rank = 1, CounterPart = x }));
rankedItems.AddRange(items2.Select(x =&gt; new CounterPartRanking { Rank = 2, CounterPart = x }));
rankedItems.AddRange(items3.Select(x =&gt; new CounterPartRanking { Rank = 3, CounterPart = x }));
rankedItems.AddRange(items4.Select(x =&gt; new CounterPartRanking { Rank = 4, CounterPart = x }));
rankedItems.AddRange(items5.Select(x =&gt; new CounterPartRanking { Rank = 5, CounterPart = x }));
rankedItems.AddRange(items6.Select(x =&gt; new CounterPartRanking { Rank = 6, CounterPart = x }));
rankedItems.AddRange(items7.Select(x =&gt; new CounterPartRanking { Rank = 7, CounterPart = x }));


Other comments

As I mentioned previously, this isn't gauranteed to be correct (but is in every instance I know of):

var items = rankedItems.OrderBy(x =&gt; x.Rank).Select(x =&gt; x.CounterPart);
var distinct = items.Distinct();