tags:

views:

269

answers:

4

Is there any way to clean up this type of loop using LINQ?

  List<Car> result;
  List<string> makes;
  List<string> models;

  for (int i = 0; i < makes.Count() && i < models.Count(); i++)
  {
    result.Add(new Car() { Make = makes[i], Model = models[i] });
  }

Basically I'm looking for some way to collate multiple arrays of individual fields into a single array of objects made up of those fields.

+7  A: 

You could use Enumerable.Range, like so:

List<Car> result = Enumerable.Range(0, Math.Min(makes.Count, models.Count))
    .Select(i => new Car { Make = makes[i], Model = models[i] }).ToList();

If makes and models always contain the same number of items, you can use more compact syntax:

List<Car> result = makes.Select((make, i) => new Car { Make = make, Model = models[i] }).ToList();
Bradley Grainger
This works but I was hoping there would be something more syntactically compact.
Luke
I've updated the answer to show an overload of Select() that allows more compact syntax.
Bradley Grainger
Need a ToList() on the second answer. +1
David B
+1  A: 

Depending on how frequently you are going to need to write LINQ queries against these arrays, it may be worth building a class that wraps the arrays and implements IEnumerable<Car>. Then your code looks like this:

IEnumerable<Car> cars = new MyClass(makes, models);
var result = from cars...

It's not especially complicated to build this; it's just four simple methods (well, six, if you count constructors) spread across two classes (you also need to implement IEnumerator<Car>).

This approach keeps you from having to embed implementation details in all of your LINQ queries. Also, if you've never done this, it's really something worth learning. Being comfortable implementing IEnumerable<T> significantly expands the space of things that you can readily use LINQ for.

Robert Rossney
+4  A: 

It sounds like you really need a new LINQ operator - one which wasn't included in LINQ, somewhat accidentally: Zip. It would basically take two enumerables and return a single enumerable which paired entries, until one or other of the original sequences finished.

I don't have anything like that up my sleeve, but it shouldn't take too long to write if you're interested. The resulting code would be something like:

List<Car> cars = makes.Zip(models)
                      .Select(pair => new Car(pair.First, pair.Second))
                      .ToList();

Let me know if you'd be interested in this and I'll code it up in MiscUtil.

Jon Skeet
Actually this would be perfect, even better if it could "Zip" more than just 2 enumerables.
Luke
+1  A: 
List<Car> result = makes
                   .Take(model.Count)
                   .Select((make, index) => new Car {Make = make, Model = models[index]});

Note that this works even if makes and models aren't the same length.

hwiechers