Legion
Legion

Reputation: 3427

Using Where and ForEach to modify specific elements in a list

If I have a list of objects that have the properties fruitName and numFruits and I want to pluralize the fruitName where numFruits is greater than 1, is it possible to do that in a single statement by chaining together Where and Foreach?

Something like:

fruitList.Where(fl => fl.numFruits > 1).ForEach(fl => fl.fruitName = fl.fruitName + "s");

I tried the above and it doesn't work. It complains that System.Collections.Generic.IEnumerable doesn't contain a definition for ForEach.

Upvotes: 0

Views: 109

Answers (6)

Bill
Bill

Reputation: 14685

Given that "append an s" doesn't actually give you the correct answer for many fruits, any approach that does that will give you an incorrect answer, no matter how well it does it.

Consider using a lookup table to map singlular to plurals (and vice versa) instead:

using System;
using System.Collections.Generic;
using System.Linq;

public class Test
{
    private static Dictionary<string, string> fruitLookup = 
        new Dictionary<string, string>
    {
        {"blueberry", "blueberries"},
        {"peach", "peaches"},
        {"apple", "apples"}
    };

    public static void Main()
    {
        var fruitList = new List<string> {"blueberry", "peach", "apple"};

        // Here is your one-line conversion:
        var plurals = fruitList.Select(f => fruitLookup[f]).ToList();

        foreach (var p in plurals)
        {
            Console.WriteLine(p);
        }
    }
}

Upvotes: 0

ryanyuyu
ryanyuyu

Reputation: 6486

If you really want a one-liner (it will be harder to maintain) and want to keep the original list intact but only modify some of the elements, you'll have to use a full anonymous function. If you need multiple statements (a block of code), you'll need to include the braces and statement-terminating semicolons like below:

fruitList.ForEach(fl => { fl.fruitName = fl.numFruits > 1 ? fl.fruitName + "s" : fl.fruitName; });

This works on the original list (no subset) and does basically the exact same thing a structured foreach would do.

Upvotes: 2

Guvante
Guvante

Reputation: 19203

Typically you want to use foreach the language construct when possible. Eric Lippert has a blog post going into additional detail as to why.

Loops are good when you are doing modifications as it makes finding those modifications easier.

foreach (var fl in fruitList.Where(fl => fl.numFruits > 1))
{
    fl.fruitName = fl.fruitName + "s";
}

Is more straightforward and accomplishes the same task.

Upvotes: 3

RagtimeWilly
RagtimeWilly

Reputation: 5445

There's a good blog post by Eric Lippert on why there is no “ForEach” sequence operator extension method, essentially the reason is:

The first reason is that doing so violates the functional programming principles that all the other sequence operators are based upon. Clearly the sole purpose of a call to this method is to cause side effects. The purpose of an expression is to compute a value, not to cause a side effect. The purpose of a statement is to cause a side effect. The call site of this thing would look an awful lot like an expression (though, admittedly, since the method is void-returning, the expression could only be used in a “statement expression” context.) It does not sit well with me to make the one and only sequence operator that is only useful for its side effects.

If you wanted to do this in a single statement you could use a .Select()

var newFruitList = fruitList.Where(fl => fl.numFruits > 1).Select(fl => fl.fruitName + "s");

Upvotes: 1

Bilal Bashir
Bilal Bashir

Reputation: 1493

You can use the static Array.ForEach method to update the list.

Array.ForEach(fruitList.Where(fl => fl.numFruits > 1).ToArray(), x => { x.fruitName += "s"; });

Upvotes: 0

Frank Ibem
Frank Ibem

Reputation: 834

Like @Tim Schmelter suggested, you can use ToList() to convert to a list and then use the ForEach method on the result returned. Although the ToList() might return a shorter list based on the filter, the original objects themselves would be changed and your fruitList will remain unchanged.

fruitList.Where(fl => fl.numFruits > 1).ToList().ForEach(fl => fl.fruitName = fl.fruitName + "s");
// fruitList still has all elements

Upvotes: 0

Related Questions