Which extension method to use to filter Enumerable

  • Thread starter Thread starter cody
  • Start date Start date
C

cody

Hi I have a list of T. Iam looking for an extension method which returns
an Enumerator which returns only elements which fullfill a certain
condition:

Is there such an extension method like the "GiveItToMe" which is a
placeholder here for me.
I found Where() and Select() but their predicates seemed to base on the
element's index, not the element itself.


foreach (T t in list.GiveItToMe(element=>element.Enabled))
{
t.Doit();
}
 
cody said:
Hi I have a list of T. Iam looking for an extension method which returns
an Enumerator which returns only elements which fullfill a certain
condition:

Is there such an extension method like the "GiveItToMe" which is a
placeholder here for me.
I found Where() and Select() but their predicates seemed to base on the
element's index, not the element itself.
That's because you didn't look carefully enough. .Where() and .Select() have
overloads that allow you to involve the index as well, but these are optional.
foreach (T t in list.GiveItToMe(element=>element.Enabled))
{
t.Doit();
}
..Where() will work:

foreach (T t in list.Where(element => element.Enabled)) {

Or you could write this, which is the same thing:

foreach (from t in list where t.Enabled select t) {

You may or may not find this easier to read.

For completeness, the .Where() that involves the index would look like this:

foreach (T t in list.Where((index, element) => element.Enabled)) {

Obviously there is no point to this if you're not using the index.

See also http://msdn.microsoft.com/vcsharp/aa336746 .
 
Jeroen said:
That's because you didn't look carefully enough. .Where() and .Select()
have overloads that allow you to involve the index as well, but these
are optional.

.Where() will work:

foreach (T t in list.Where(element => element.Enabled)) {

Or you could write this, which is the same thing:

foreach (from t in list where t.Enabled select t) {

You may or may not find this easier to read.

For completeness, the .Where() that involves the index would look like
this:

foreach (T t in list.Where((index, element) => element.Enabled)) {

Obviously there is no point to this if you're not using the index.

See also http://msdn.microsoft.com/vcsharp/aa336746 .

Thank you very much!
 
Jeroen said:
That's because you didn't look carefully enough. .Where() and .Select()
have overloads that allow you to involve the index as well, but these
are optional.

.Where() will work:

foreach (T t in list.Where(element => element.Enabled)) {

Or you could write this, which is the same thing:

foreach (from t in list where t.Enabled select t) {

You may or may not find this easier to read.

For completeness, the .Where() that involves the index would look like
this:

foreach (T t in list.Where((index, element) => element.Enabled)) {

Obviously there is no point to this if you're not using the index.

See also http://msdn.microsoft.com/vcsharp/aa336746 .

I tried to get this functionality with out using a loop.
First I had:

mylist.ForEach(e => { if (e.Import)e.Method.Invoke(); });

Which worked , but then I decided that it would be better to separate
condition and action:

mylist.Where(e => e.Import).ForEach(e => e.Method.Invoke());

But it doesn't compile because there is no ForEach method on Enumerable.
Is there a way to accomplish this or do I have to write extension
methods for myself for such trivial things?
 
cody said:
I tried to get this functionality with out using a loop.

Well, think about it: it's impossible to do this without a loop -- even if
you don't write one, you're going to be calling a method that uses one. So
why fret over having to loop yourself? It's not even more work -- unlike
writing out enumerators.
First I had:

mylist.ForEach(e => { if (e.Import)e.Method.Invoke(); });

Which worked , but then I decided that it would be better to separate
condition and action:

mylist.Where(e => e.Import).ForEach(e => e.Method.Invoke());

But it doesn't compile because there is no ForEach method on Enumerable.
Is there a way to accomplish this or do I have to write extension
methods for myself for such trivial things?
In my opinion, it's better to have an explicit foreach statement, exactly
because you want to separate identifying the elements and performing actions
on them. I'd write it like this:

var importMethods = from e in mylist where e.Import select e.Method;
foreach (var importMethod in importMethods) importMethod.Invoke();

Obviously much more keystrokes, but naming both the collection and the
element also makes it easier to read and debug.

I think List.ForEach() is a mistake, needlessly replicating a basic language
feature. I don't know if the LINQ designers agree with me, but that
Enumerable.ForEach() is missing may be indicative of something.

Also, if you want a real functional language, try F#. Just because C# + LINQ
approximates one doesn't mean you have to write everything like that. :-)
 
Jeroen said:
Well, think about it: it's impossible to do this without a loop -- even
if you don't write one, you're going to be calling a method that uses
one.

I'd never doubt this :-D
So why fret over having to loop yourself? It's not even more work
-- unlike writing out enumerators.

Well OK I found that it is less typing and looks better ^^
In my opinion, it's better to have an explicit foreach statement,
exactly because you want to separate identifying the elements and
performing actions on them. I'd write it like this:

var importMethods = from e in mylist where e.Import select e.Method;
foreach (var importMethod in importMethods) importMethod.Invoke();

Obviously much more keystrokes, but naming both the collection and the
element also makes it easier to read and debug.

Maybe you are right :-(
I just was curious and was testing out the limits what is possible with
the new .NET framework.
I think List.ForEach() is a mistake, needlessly replicating a basic
language feature. I don't know if the LINQ designers agree with me, but
that Enumerable.ForEach() is missing may be indicative of something.

Also, if you want a real functional language, try F#. Just because C# +
LINQ approximates one doesn't mean you have to write everything like
that. :-)

Couldn't agree more. There is absolutely no reason why there is a
ForEach() method on ArrayList but not on IEnumerable.

Reminds me on the fact that you had a String.Contains(Char) method but
not String.Contains(String) in .NET 2.0, which seems to be fixed in 3.5.

List<T>.ForEach() now returns void, it would be cool if it returned
Enumerable<T> so one could nicely chain lots of stuff or maybe we could
use ForEach() in LINQ queries for pre transforming data before using it.

But I think that adding ForEach() to the framework now would be a
breaking change as the call it, since there is already an implementation
in class List :(
 
cody said:
There is absolutely no reason why there is a ForEach() method on
ArrayList but not on IEnumerable.
Well, my point is that there shouldn't have been a .ForEach() method on
*anything*. If two ways are provided to do the same thing, there should be
clear situational advantages in using one or the other. .ForEach() versus
the foreach statement definitely doesn't qualify -- foreach can do
everything .ForEach() can and more. Enumeration is such a fundamental part
of the framework that it seems like whoever added .ForEach() wasn't really
thinking about the usefulness, just adding something because they could.
List<T>.ForEach() now returns void, it would be cool if it returned
Enumerable<T> so one could nicely chain lots of stuff or maybe we could
use ForEach() in LINQ queries for pre transforming data before using it.
But that already exists -- it's called .Select(), and the idea is that it
*doesn't* change the objects themselves but returns the result of some
function applied to them. Of course, there's nothing to prevent you from
breaking the rules and modifying the objects in the .Select() delegate, but
introducing side effects like this makes code much harder to understand (and
also not thread-safe, but that's another topic).

If you really want an in-place transformational approach, that's easy
enough, but you'd have to define it on IList<T>, not IEnumerable<T>,
otherwise you couldn't replace the elements:

public static IList<T> Transform<T>(this IList<T> list, Func<T, T>
transform) {
for (int i = 0; i != list.Count; ++i) {
list = transform(list);
}
return list;
}

Now even if your method isn't changing the object identity, you could still
use it:

myList.Where(e => e.Import).Transform(e => { e.Method.Invoke(); return e; });

And if you do this often you could provide another method, and this one
could work on IEnumerable:

public static IEnumerable<T> Apply<T>(this IEnumerable<T> sequence,
Action<T> action) {
foreach (T t in sequence) action(t);
return sequence;
}

So now you can chain to your heart's content:

myList.Where(e => e.Import).Select(e => e.Method).Apply(m =>
m.Invoke()).Apply(m => m.Invoke());

This approach is limited in that, unlike .Select(), it can't produce
sequences with a different type. And while you can chain, it's a little
pointless, because you could just fold all the transformations into one
delegate that executes them sequentially. Or, indeed, use foreach:

foreach (var m in from e in myList where e.Import select e.Method) {
m.Invoke();
m.Invoke();
}

I know which one I prefer. This is C#, not JQuery. :-)
But I think that adding ForEach() to the framework now would be a
breaking change as the call it, since there is already an implementation
in class List :(
Actually, no, thanks to the rules for extension methods. Instance methods
always take precedence over extension methods, so List<T>.ForEeach() would
not clash with Enumerable.ForEach().
 
Back
Top