78

I have some list (where T is a custom class, and class has some properties). I would like to know how to change one or more values inside of it by using Lambda Expressions, so the result will be the same as the foreach loop bellow:

NOTE: list contains multiple items inside (multiple rows)

        foreach (MyClass mc in list)  
        {
            if (mc.Name == "height")
                mc.Value = 30;
        }

And this the the linq query (using Lambda expressions), but its not the same as the upper foreach loop, it only returns 1 item (one row) from the list!

What I want is, that it returns all the items (all rows) and ONLY changes the appropriate one (the items specified in the WHERE extension method(s).

list = list.Where(w => w.Name == "height").Select(s => { s.Value = 30; return s; }).ToList();

NOTE: these 2 example are not the same! I repeat, the linq only returns 1 item (one row), and this is something I don't want, I need all items from the list as well (like foreach loop, it only do changes, but it does not remove any item).

Donny V.
  • 22,248
  • 13
  • 65
  • 79
Mitja Bonca
  • 4,268
  • 5
  • 24
  • 30
  • 6
    *Why* do you want to use lambda expressions? The `foreach` code works fine and is simple. LINQ is for *querying* data, not mutating it. – Jon Skeet Oct 20 '12 at 08:43
  • 2
    Where, by definition, only returns matching records. Basically: that isn't what you want - just use foreach – Marc Gravell Oct 20 '12 at 08:50
  • Hi Jon, I know foreach loop if batter and faster, but I would like t learn, thats all. For some "small" codes will do just fine. But the point is mostly learing. – Mitja Bonca Oct 20 '12 at 09:08
  • Thx Marc, nice and simple explanation. – Mitja Bonca Oct 20 '12 at 09:13
  • @JonSkeet I disagree with your statement. Lambdas help to do functional programming within c#. Also while querying data, you might want to mutate it, to get it in the form you want it, e.g. for a viewmodel. – Tigerware Jun 25 '19 at 08:38
  • 2
    @BluE: That's not a functional approach though. Creating a *new* object in the form you want it is the functional approach. – Jon Skeet Jun 25 '19 at 08:48

7 Answers7

149

You could use ForEach, but you have to convert the IEnumerable<T> to a List<T> first.

list.Where(w => w.Name == "height").ToList().ForEach(s => s.Value = 30);
McGarnagle
  • 101,349
  • 31
  • 229
  • 260
15

I'd probably go with this (I know its not pure linq), keep a reference to the original list if you want to retain all items, and you should find the updated values are in there:

 foreach (var mc in list.Where(x => x.Name == "height"))  
     mc.Value = 30;
gmn
  • 4,199
  • 4
  • 24
  • 46
6

You could use a projection with a statement lambda, but the original foreach loop is more readable and is editing the list in place rather than creating a new list.

var result = list.Select(i => 
   { 
      if (i.Name == "height") i.Value = 30;
      return i; 
   }).ToList();

Extension Method

public static IEnumerable<MyClass> SetHeights(
    this IEnumerable<MyClass> source, int value)
{
    foreach (var item in source)
    {
       if (item.Name == "height")
       {
           item.Value = value;
       }

       yield return item;
    } 
}

var result = list.SetHeights(30).ToList();
devdigital
  • 34,151
  • 9
  • 98
  • 120
  • Is it possible to avoid if block? To maybe define Select extension method? – Mitja Bonca Oct 20 '12 at 08:50
  • Yes you could define a custom extension method, but the original foreach is less obscure and easier to maintain, and won't create a new list which may be costly. – devdigital Oct 20 '12 at 08:58
  • I know how to do a cusom extemsion method, I was only wondering if is possible to do it in a "short" way, regarding the code?! Like the example @Robin showed, just that his code does not work! – Mitja Bonca Oct 20 '12 at 09:03
  • No, the shortest example that meets your requirements and using LINQ is with a projection and statement lambda, which you could wrap up in a custom extension method as shown. – devdigital Oct 20 '12 at 09:11
  • Shouldn't SetHeights return `IEnumerator` instead of `void`? – AlexandreG Sep 24 '18 at 21:42
4

How about list.Find(x => x.Name == "height").Value = 20; This works fine. I know its an old post, but just wondered why hasn't anyone suggested this? Is there a drawback in this code?

2

This is the way I would do it : saying that "list" is a <List<t>> where t is a class with a Name and a Value field; but of course you can do it with any other class type.

    list = list.Where(c=>c.Name == "height")
        .Select( new t(){Name = c.Name, Value = 30})
        .Union(list.Where(c=> c.Name != "height"))
        .ToList();

This works perfectly ! It's a simple linq expression without any loop logic. The only thing you should be aware is that the order of the lines in the result dataset will be different from the order you had in the source dataset. So if sorting is important to you, just reproduce the same order by clauses in the final linq query.

David
  • 143
  • 8
0

I know this is an old post but, I've always used an updater extension method:

      public static void Update<TSource>(this IEnumerable<TSource> outer, Action<TSource> updator)
        {
            foreach (var item in outer)
            {
                updator(item);
            }
        }

list.Where(w => w.Name == "height").ToList().Update(u => u.height = 30);

Stu
  • 71
  • 3
-2

You can do something like this:

var newList = list.Where(w => w.Name == "height")
              .Select(s => new {s.Name, s.Value= 30 }).ToList();

But I would rather choose to use foreach because LINQ is for querying while you want to edit the data.

Robin V.
  • 1,484
  • 18
  • 30
  • I trieed with this one too before, but I got an exception: "Invalid anonymous type member declarator. Anonymous type members must be declared with a member assignment, simple name or member access.". This is for "s.Value=30". – Mitja Bonca Oct 20 '12 at 08:56