tags:

views:

141

answers:

4

Hi!

I wanna do something like this:

List<string> list = new List<string>();
... put some data in it ...

list.CallActionForEachMatch(x=>x.StartsWith("a"), ()=> Console.WriteLine(x + " matches!"););

Syntax: CallActionForEachMatch(Criteria, Action)

How is this possible? :)

+3  A: 
list.FindAll(x=>x.StartsWith("a"))
    .ForEach(x =>  Console.WriteLine(x + " matches!"));

Or you can write your own CallActionForEachMatch extension:

public static void CallActionForEachMatch<T>(this IEnumerable<T> values, Func<T, bool> pred, Action<T> act)
{
    foreach (var value in values.Where(pred))
    {
        act(value);
    }
}
Yuriy Faktorovich
+2  A: 

An extension method something like this:

static public void CallActionForEachMatch(this List<T> list, Func<T, bool> criteria, Action<T> action)
{
    list.Where(criteria).ToList().ForEach(action);
}
David M
There is no `ForEach` method on `IEnumerable<T>` (the result of `list.Where`).
Jason
This will not work, you need to use `CallActionForEachMatch<T>`, and without another extension the ForEach is for List<T> not IEnumerable<T>.
Yuriy Faktorovich
I did say something like this. ;) Fixed...
David M
@David M: Good. :-) Downvote removed.
Jason
+3  A: 

Write extension methods:

static class IEnumerableForEachExtensions {
    public static void ForEachMatch<T>(this IEnumerable<T> items,
        Predicate<T> predicate,
        Action<T> action
    ) {
        items.Where(x => predicate(x)).ForEach(action);
    }

    public static void ForEach<T>(this IEnumerable<T> items, Action<T> action) {
        foreach(T item in items) {
            action(item);
        }
    }
 }

Usage:

// list is List<string>
list.ForEachMatch(s => s.StartsWith("a"), s => Console.WriteLine(s));

Note this is fully general as it will eat any IEnumerable<T>. Note that there are some that would consider this an abuse of LINQ because of the explicit side effects.

Jason
what do you mean by side effects?
Rookian
@Rookian LINQ is "Language Integrated **Query** " - "query" is read-only, meaning it does not affect the sequence it is operating against, or any of the members in that sequence (called "side effects"). What you're describing does. Thus LINQ is not the most appropriate choice.
Rex M
A side effect is when state is modified (including interaction with the outside world). It's a big no-no in functional programming which is what LINQ is modeled after. Another issue is that there are closure semantics that can occur when using methods like this which can alter the lifetime of objects. Sometimes old-fashioned imperative code is best. :-)
Jason
+4  A: 

I wouldn't; I'd just use:

foreach(var item in list.Where(x=>x.StartsWith("a"))) {
    Console.WriteLine(item + " matches!");
}

But you could use:

list.FindAll(x=>x.StartsWith("a"))
    .ForEach(item=>Console.WriteLine(item + " matches!"));
Marc Gravell
why do you would not use this (because of readibility, performance ...) ?
Rookian
I don't like the `FindAll` or `Where.ToList` approach as it introduces an extra list for no purpose, and I just can't see that a `ForEach` extension method adds enough to justify changing the environment - perhaps introduces captures, etc. LINQ is about **query**, not side-effects. Not including Parallel.ForEach, which I'll forgive ;-p
Marc Gravell
MoreLinq? More charecters.
Yuriy Faktorovich
Belated congrats on the 100k
Chris S