Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to efficiently limit and then concatenate a result with a linq / lambda expression?

Tags:

c#

concat

lambda

I am in the process of creating a service to make it easy for a user to select a protocol from the IANA - Protocol Registry.

As you might imagine searching the registry for the term http pulls up a lot of hits. Since amt-soap-http is going to selected by a user much less frequently than straight http I decided that it would be a good idea to pull out everything that starts with http and then concatenate that with the remaining results.

The below lambda expression is the result of that thought process:

var records = this._ianaRegistryService.GetAllLike(term).ToList();
var results = records.Where(r => r.Name.StartsWith(term))
                     .OrderBy(r => r.Name)
                     .Concat(records.Where(r => !r.Name.StartsWith(term))
                                    .OrderBy(r => r.Name))
                     .Take(MaxResultSize);

Unfortunately, I feel like I am iterating through my results more times than necessary. Premature optimization considerations aside is there a combination of lambda expressions that would be more efficient than the above?

like image 303
ahsteele Avatar asked Feb 14 '13 21:02

ahsteele


2 Answers

It might be more efficient as a two-step ordering:

var results = records.OrderBy(r => r.Name.StartsWith(term) ? 1 : 2)
                     .ThenBy(r => r.Name)
                     .Take(MaxResultSize);
like image 78
D Stanley Avatar answered Nov 06 '22 04:11

D Stanley


Using comment to explain what I am trying to do is getting hard. So i will post this another answer. Suppose I want to sort a list of random integers first according to its being even or odd then in numerical order (simulating StartsWith with mod 2).

Here is the test case: action2 is the same as other answer.

If you run this code you will see that my suggestion (action1) is two times faster.

void Test()
{
    Random rnd = new Random();
    List<int> records = new List<int>();
    for(int i=0;i<2000000;i++)
    {
        records.Add(rnd.Next());
    }

    Action action1 = () =>
    {
        var res1 = records.GroupBy(r => r % 2)
                    .OrderBy(x => x.Key)
                    .Select(x => x.OrderBy(y => y))
                    .SelectMany(x => x)
                    .ToList();
    };

    Action action2 = () =>
    {
        var res2 = records.OrderBy(x => x % 2).ThenBy(x => x).ToList();
    };


    //Avoid counting JIT
    action1();
    action2();


    var sw = Stopwatch.StartNew();
    action1();
    long t1 = sw.ElapsedMilliseconds;

    sw.Restart();
    action2();
    long t2 = sw.ElapsedMilliseconds;

    Console.WriteLine(t1 + " " + t2);
}
like image 21
I4V Avatar answered Nov 06 '22 04:11

I4V