Shaul Behr
Shaul Behr

Reputation: 37993

Using Linq to run a method on a collection of objects?

This is a long shot, I know...

Let's say I have a collection

List<MyClass> objects;

and I want to run the same method on every object in the collection, with or without a return value. Before Linq I would have said:

List<ReturnType> results = new List<ReturnType>();
List<int> FormulaResults = new List<int>();
foreach (MyClass obj in objects) {
  results.Add(obj.MyMethod());
  FormulaResults.Add(ApplyFormula(obj));
}

I would love to be able to do something like this:

List<ReturnType> results = new List<ReturnType>();
results.AddRange(objects.Execute(obj => obj.MyMethod())); 
// obviously .Execute() above is fictitious
List<int> FormulaResults = new List<int>();
FormulaResults.AddRange(objects.Execute(obj => ApplyFormula(obj)));

I haven't found anything that will do this. Is there such a thing?

If there's nothing generic like I've posited above, at least maybe there's a way of doing it for the purposes I'm working on now: I have a collection of one object that has a wrapper class:

class WrapperClass {
  private WrappedClass wrapped;
  public WrapperClass(WrappedClass wc) {
    this.wrapped = wc;
  }
}

My code has a collection List<WrappedClass> objects and I want to convert that to a List<WrapperClass>. Is there some clever Linq way of doing this, without doing the tedious

List<WrapperClass> result = new List<WrapperClass>();
foreach (WrappedClass obj in objects)
  results.Add(new WrapperClass(obj));

Thanks...

Upvotes: 16

Views: 20770

Answers (6)

Anya Hope
Anya Hope

Reputation: 1361

You can also run a custom method using the marvelous Jon Skeet's morelinq library

For example if you had a text property on your MyClass that you needed to change in runtime using a method on the same class:

objects = objects.Pipe<MyClass>(class => class.Text = class.UpdateText()).ToList();

This method will now be implemented on every object in your list. I love morelinq!

Upvotes: 1

LBushkin
LBushkin

Reputation: 131656

I think that the Select() extension method can do what you're looking for:

objects.Select( obj => obj.MyMethod() ).ToList(); // produces List<Result> 

objects.Select( obj => ApplyFormula(obj) ).ToList(); // produces List<int>

Same thing for the last case:

objects.Select( obj => new WrapperClass( obj ) ).ToList();

If you have a void method which you want to call, here's a trick you can use with IEnumerable, which doesn't have a ForEach() extension, to create a similar behavior without a lot of effort.

objects.Select( obj => { obj.SomeVoidMethod(); false; } ).Count();

The Select() will produce a sequence of [false] values after invoking SomeVoidMethod() on each [obj] in the objects sequence. Since Select() uses deferred execution, we call the Count() extension to force each element in the sequence to be evaluated. It works quite well when you want something like a ForEach() behavior.

Upvotes: 4

jason
jason

Reputation: 241583

If the method MyMethod that you want to apply returns an object of type T then you can obtain an IEnumerable<T> of the result of the method via:

var results = objects.Select(o => o.MyMethod());

If the method MyMethod that you want to apply has return type void then you can apply the method via:

objects.ForEach(o => o.MyMethod());

This assumes that objects is of generic type List<>. If all you have is an IEnumerable<> then you can roll your own ForEach extension method or apply objects.ToList() first and use the above syntax .

Upvotes: 2

Martin
Martin

Reputation: 5452

The C# compiler maps a LINQ select onto the .Select extension method, defined over IEnumerable (or IQueryable which we'll ignore here). Actually, that .Select method is exactly the kind of projection function that you're after.

LBushkin is correct, but you can actually use LINQ syntax as well...

var query = from o in objects
   select o.MyMethod();

Upvotes: 1

Yuriy
Yuriy

Reputation:

http://www.hookedonlinq.com/UpdateOperator.ashx has an extended Update method you can use. Or you can use a select statement as posted by others.

Upvotes: 0

Marc Gravell
Marc Gravell

Reputation: 1062520

Would:

results.AddRange(objects.Select(obj => ApplyFormula(obj)));

do?

or (simpler)

var results = objects.Select(obj => ApplyFormula(obj)).ToList();

Upvotes: 22

Related Questions