views:

212

answers:

1

Hi folks,

i'm not sure this is the best way to do the following code. I'm not sold on a foreach inside another foreach. Could this be done *better** with Linq?

*I understand that better could be either
a) more performant
b) easier to read / more elegant
c) all of the above

NOTE: .NET 3.5 solutions accepted :) NOTE2: the two IList's were the results of a multi-recordset stored procedure, via Linq2Sql.

here's the make believe code:

// These two lists are the results from a IMultipleResults Linq2Sql stored procedure.
IList<Car> carList = results.GetResult<Car>().ToList();
IList<Person> people = results.GetResult<Person>().ToList();

// Associate which people own which cars.
foreach(var person in people)
{
    var cars = (from c in cars
                where c.CarId == person.CarId
                select c).ToList();

    foreach (var car in cars)
    {
        car.Person = person;    
    }
}

Cheers :)

+6  A: 

I don't think performance would be any different but if you're looking for terseness:

var q = from person in people
        join car in cars on person.CarId equals car.CarId
        select new { car, person };
foreach(var o in q)
{
  o.car.Person = o.person; 
}

Edit: After Jon's hint on this version being faster I got curious and profiled both functions. This version seems twice as fast which is amazing. I checked the disassembly. The overhead of the original implementation seems to come from new enumerators getting created for both outer and inner loop, causing P times new/dispose overhead.

For this code only one Enumerator is created which I think is the magic of "Join" function. I didn't examine how it works though.

ssg
I believe this *will* perform better, as it will be using a hashtable inside the join - the complexity will be O(P+C) instead of O(P*C) I *think*.
Jon Skeet
That is very funky. +1
Matt Hamilton
Jon it seems it's really faster. It looks like you're right in O(P+C) vs O(P*C) thing since this version creates a single enumerator after join runs while original works with two outer/inner enumerators. I added my findings to the answer.
ssg
awesausce, boys! thanks heaps :)
Pure.Krome
Oh guys -- one more question on this. Could this answer be updated to be an extension method (or at least a static method?)
Pure.Krome
Why is that needed?
ssg
to make it more generic, if possible. it's a really kewl little bit of code and i was wondering if it could be possible to do somethign like that. maybe not.
Pure.Krome
If you make your code generic, I'll make mine. Otherwise answer and question don't correlate.
ssg
@Jon, you are the man. It is amazing that there are people who knows these kinds of things off the top of their heads.
Alex Baranosky