tags:

views:

25

answers:

1

I'm trying to test just how far LINQ can really go. What i'm trying to achieve is property assignments on a list of objects with a single expression rather than for loops. I want to take all items in listA and update the IsMatched property, but only where there is a corresponding item in listB (which is a different type), is this possible?

Sample Code:

public struct A { public int x; public bool IsMatched;}
public struct B {public int x;}

static void Main(string[] args)
{
  List<A> listA = new List<A>(); 
  List<B> listb = new List<B>();
  listA.Add(new A() { x=1}); 
  listA.Add(new A() { x=2}); 
  listA.Add(new A() { x=3});

  listb.Add(new B() { x=2}); 
  listb.Add(new B() { x=3});

  listA = listA.SelectMany(fb => listb, (fb, j) => new {a=fb, b=j})
        .Where (anon => anon.b.x == anon.a.x).Select(anon => new A() {x=anon.a.x, IsMatched=true})
        .ToList(); // this does not do what I want.

}

I've tried using SelectMany but this only returns the items that matched, or a Cartesian product which I don't want.

+3  A: 

LINQ wasn't designed to cause side-effects. In this case, modify items in a collection. Just create the query to select the items you want to modify, then loop through the items modifying them as necessary.

var query = from a in listA
            join b in listB on a.x equals b.x
            select a;
foreach (var a in query)
    a.IsMatched = true;

You can cheat however and formulate a lambda to cause the side-effect. Then use it an an aggregation method. But you shouldn't do this in practice however.

(from a in listA
 join b in listB on a.x equals b.x
 let modify = new Func<A,A>(m => { m.IsMatched = true; return m; })
 select modify(a)).ToArray();
Jeff M
@Jeff M - Thanks - Apparently assignments can be done inside of an anonymous method check [this](http://stackoverflow.com/questions/807797/linq-select-an-object-and-change-some-properties-without-creating-a-new-object) out, just read it myself. This gets me halfway at least.
James
@James: Right, like I've shown in my second example. ;)
Jeff M
@Jeff M - Sorry I need to read more carefully next time ;). Why is doing that considered a bad practice?
James
@James: As mentioned in the first part, ;) LINQ wasn't really designed to do cause side affects, just operate on sets of data.
Jeff M
@Jeff M - That doesn't really explain why it's a bad practice, given the fact that it can be done without much hassle.
James
@James: Just because there's nothing stopping you, doesn't mean you should do it. I'm not sure I can adequately explain why. Perhaps [Eric Lippert's](http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx) article which explains why there isn't a `ForEach()` extension method (which is effectively what we're doing here) can explain enough.
Jeff M