59

I am new to Linq.

I want to set two values in foreach statement like this

My actual code is this

foreach (Employee emp in employees)
{
    foreach(Department dept in emp.Departments)
    {
        dept.SomeProperty = null;
    }
    collection.AddRange(emp.Departments);              
}

Little refactoring turns the above into this

foreach (Employee emp in employees)
{
    emp.Departments.ToList().ForEach(u => u.SomeProperty = null))
    collection.AddRange(emp.Departments);              
}

But I want something like this

employees.ToList().Foreach(collection.AddRange(emp.Departments),
emp.Departments.ToList().ForEach(u => u.SomeProperty = null))
  
Neuron
  • 5,141
  • 5
  • 38
  • 59
manav inder
  • 3,531
  • 16
  • 45
  • 62
  • 32
    NO! Don't convert your collection to a list just to call that method... Use your loops. – Jeff Mercado Oct 19 '11 at 05:15
  • 1
    Think about performance too while changing to Linq. Not always LINQ is helpful compared to normal loops. – Zenwalker Oct 19 '11 at 05:17
  • 8
    Do that and nobody can read your code again. Self obfuscation. – Ahmet Kakıcı Oct 19 '11 at 05:18
  • 44
    The original code is perfectly clear and understandable; I see no need to change it. Remember, the purpose of code is not just to communicate to the *compiler* it is to communicate to the future reader of the code; make it as clear as possible. – Eric Lippert Oct 19 '11 at 14:30
  • 3
    @EricLippert Thank you Eric. Had I not been accustomed to READING all comments now I would have missed this valuable recommendation. – Stephen Patten Mar 14 '13 at 17:21

8 Answers8

85

You shouldn't use ForEach in that way. Read Lippert's “foreach” vs “ForEach”

If you want to be cruel with yourself (and the world), at least don't create useless List

employees.All(p => {
    collection.AddRange(p.Departments);
    p.Departments.All(u => { u.SomeProperty = null; return true; } );
    return true;
});

Note that the result of the All expression is a bool value that we are discarding (we are using it only because it "cycles" all the elements)

I'll repeat. You shouldn't use ForEach to change objects. LINQ should be used in a "functional" way (you can create new objects but you can't change old objects nor you can create side-effects). And what you are writing is creating so many useless List only to gain two lines of code...

openshac
  • 4,966
  • 5
  • 46
  • 77
xanatos
  • 109,618
  • 12
  • 197
  • 280
  • 23
    Although what we're suggesting here is to replace the `ToList()` abuse with an `All` abuse. ;) – Ilian Oct 19 '11 at 05:32
  • 2
    @IlianPinzon I thought I was quite clear of this. Do you think I should **bold** it? :-) – xanatos Oct 19 '11 at 05:33
  • 1
    I prefer a blinking marquee that says "DO NOT (AB)USE." :) Newbies may read the example as gospel if they skip the disclaimer text above. :) – Ilian Oct 19 '11 at 05:38
  • 1
    Atleast with removing the ToList it isn't copying the collection in memory just to iterate over it and throw it away. On large-ish collection ToList is deadly. – Phill Oct 19 '11 at 05:44
24

As xanatos said, this is a misuse of ForEach.

If you are going to use linq to handle this, I would do it like this:

var departments = employees.SelectMany(x => x.Departments);
foreach (var item in departments)
{
    item.SomeProperty = null;
}
collection.AddRange(departments);

However, the Loop approach is more readable and therefore more maintainable.

Charles Graham
  • 1,157
  • 5
  • 6
  • 1
    +1 Wow! Beautiful use of `SelectMany` (not that I would use it :-) ) – xanatos Oct 19 '11 at 05:24
  • Though it might be better to add the range after you modify them so it logically makes sense that you're adding modified items to the collection. The collection may even store copies of the items so this might not work. – Jeff Mercado Oct 19 '11 at 05:31
  • 4
    It's probably even better to remove the `AddRange` line which does an unnecessary additional traversal of the sequence. One can do `collection.Add(item)` inside the existing loop with the same result. – Ilian Oct 19 '11 at 05:35
  • @Jeff I hadn't thought of that, fixed it. – Charles Graham Oct 19 '11 at 05:35
14
employees.ToList().ForEach(
     emp=>
     {
          collection.AddRange(emp.Departments);
          emp.Departments.ToList().ForEach(u=>u.SomeProperty = null);
     });
TheBoubou
  • 19,487
  • 54
  • 148
  • 236
artwl
  • 3,502
  • 6
  • 38
  • 53
5

Try this:

foreach (var dept in employees.SelectMany(e => e.Departments))
{
   dept.SomeProperty = null;
   collection.Add(dept);
}
Ilian
  • 5,113
  • 1
  • 32
  • 41
1
employees.ToList().Foreach(u=> { u.SomeProperty = null; u.OtherProperty = null; });

Notice that I used semicolons after each set statement that is -->

u.SomeProperty = null;
u.OtherProperty = null;

I hope this will definitely solve your problem.

Ilija Dimov
  • 5,221
  • 7
  • 35
  • 42
Saurabh
  • 47
  • 1
  • 8
0

You can use Array.ForEach()

Array.ForEach(employees, employee => {
   Array.ForEach(employee.Departments, department => department.SomeProperty = null);
   Collection.AddRange(employee.Departments);
});
Neuron
  • 5,141
  • 5
  • 38
  • 59
maeneak
  • 573
  • 6
  • 10
-3

you want this?

    employees.ForEach(emp =>
    {
        collection.AddRange(emp.Departments.Where(dept => { dept.SomeProperty = null; return true; }));
    });
ojlovecd
  • 4,812
  • 1
  • 20
  • 22
-4

Try with this combination of Lambda expressions:

employees.ToList().ForEach(emp => 
{
    collection.AddRange(emp.Departments);
    emp.Departments.ToList().ForEach(dept => dept.SomeProperty = null);                    
});
S2S2
  • 8,322
  • 5
  • 37
  • 65