Apply Function to All Elements of Collection Through Linq

Apply function to all elements of collection through LINQ

A common way to approach this is to add your own ForEach generic method on IEnumerable<T>. Here's the one we've got in MoreLINQ:

public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
source.ThrowIfNull("source");
action.ThrowIfNull("action");
foreach (T element in source)
{
action(element);
}
}

(Where ThrowIfNull is an extension method on any reference type, which does the obvious thing.)

It'll be interesting to see if this is part of .NET 4.0. It goes against the functional style of LINQ, but there's no doubt that a lot of people find it useful.

Once you've got that, you can write things like:

people.Where(person => person.Age < 21)
.ForEach(person => person.EjectFromBar());

Executing a certain action for all elements in an EnumerableT

A quick-and-easy way to get this is:

Names.ToList().ForEach(e => ...);

Apply function to some elements of list

Conclusion

I did some testing on all my methods below, as well as un-lucky's answer, and the fastest of them all was option 2 below, ie

var results = theObjects.Join(idList, o => o.id, id => id, (o, id) => o).ToList();
results.ForEach(o => o.selected = true);

Another way of doing it with Linq, where we iterate around theObjects and check each one to see if its' id exists in idList:

1

var result = theObjects.ForEach(o => o.selected = idList.Contains(o.id) ? true : false);

or using Join and ForEach, where we first extract the matching items using Join and then iterate around those items:

2

var results = theObjects.Join(idList, o => o.id, id => id, (o, id) => o).ToList();
results.ForEach(o => o.selected = true);

or, you can use Select with ForEach and FirstOrDefault. This is probably going to be slower than the other 2:

3

theObjects
.Select(o => o.id)
.Where(i => idList.Contains(i)).ToList()
.ForEach(i =>
theObjects.FirstOrDefault(o => o.id == i).selected = true);

I did some testing on the 3 methods I posted, where we have 10000 MyObjects and 1000 unique ids. I ran each method 1000 times, and then got the mean ElapsedMillliseconds for each.

The results were

1

8.288 ms

2

0.19 ms

3

57.342 ms

one = 0;
two = 0;
three = 0;

for (var i = 0; i <1000; i++) {
RunTest();
}

oneMean = one / 1000;
twoMean = two / 1000;
threeMean = three / 1000;

where

private void RunTest()
{
ResetData();
var stopwatch = Stopwatch.StartNew();
theObjects.ForEach(o => o.selected = idList.Contains(o.id) ? true : false);
stopwatch.Stop();
one += stopwatch.ElapsedMilliseconds;

ResetData();
stopwatch = Stopwatch.StartNew();
var results = theObjects.Join(idList, o => o.id, id => id, (o, id) => o).ToList();
results.ForEach(o => o.selected = true);
stopwatch.Stop();
two += stopwatch.ElapsedMilliseconds;

ResetData();
stopwatch = Stopwatch.StartNew();
theObjects
.Select(o => o.id)
.Where(i => idList.Contains(i)).ToList()
.ForEach(i =>
theObjects.FirstOrDefault(o => o.id == i).selected = true);
stopwatch.Stop();
three += stopwatch.ElapsedMilliseconds;
}

private void ResetData()
{
theObjects = new List<MyObject>();
idList = new List<int>();
var rnd = new Random();

for (var i=0; i<10000; i++) {
theObjects.Add(new MyObject(){id = i});
}
for (var i=0; i<=1000; i++) {

var r = rnd.Next(0, 1000);
while (idList.Contains(r)) {
r = rnd.Next(0, 10000);
}
idList.Add(r);
}
}

I tested un-lucky's answer (most upvotes right now) and it got a mean score of 147.676

foreach(var obj in theObjects.Where(o => idList.Any(i=> i == o.id)))
{
obj.selected = true;
}

How to apply a function to every element in a list using Linq in C# like the method reduce() in python?

Assuming you're talking about this reduce function, the equivalent in C# and LINQ is Enumerable.Aggregate.

Quick example:

var list = Enumerable.Range(5, 3); // [5, 6, 7]
Console.WriteLine("Aggregation: {0}", list.Aggregate((a, b) => (a + b)));
// Result is "Aggregation: 18"

C#/Linq: Apply a mapping function to each element in an IEnumerable?

You can just use the Select() extension method:

IEnumerable<int> integers = new List<int>() { 1, 2, 3, 4, 5 };
IEnumerable<string> strings = integers.Select(i => i.ToString());

Or in LINQ syntax:

IEnumerable<int> integers = new List<int>() { 1, 2, 3, 4, 5 };

var strings = from i in integers
select i.ToString();

Apply a lambda function to a range of elements in C#

Use List<T>.ForEach if you have a List<T>

trashCans.ForEach(x => x.AddTrash(subTrashItems.GetRange(5, 3).ToArray()));

But this has one flaw, we're calling .GetRange(3, 5).ToArray() once every iteration, while we could only call it once like so:

// Either do it right at the declaration:
var subTrashItems = trashItems.GetRange(5, 3).ToArray();
trashCans.ForEach(x => x.AddTrash(subTrashItems));

// Or create a new variable and insert it again and again
var subTrashItemArray = subTrashItems.ToArray();
trashCans.ForEach(x => x.AddTrash(subTrashItemArray));

Short tangent about deferred execution in LINQ:

LINQ supports something called deferred execution, which means that your LINQ expression will only be executed once it's actually needed. This has many upsides, but one downside: it's easy to accidentally execute the same code multiple times without having to. In your example your calling .ToArray() in your loop, and it executes .GetRange(3, 5) once every loop, which is bad. Instead, call .ToArray() once when declaring subTrashItems and then you never need it again

Update all objects in a collection using LINQ

While you can use a ForEach extension method, if you want to use just the framework you can do

collection.Select(c => {c.PropertyToSet = value; return c;}).ToList();

The ToList is needed in order to evaluate the select immediately due to lazy evaluation.

How to run a function on all members of a list using linq?

Select is looking for you to return a value inside the lambda.

Try:

list.ForEach(obj => GetProcessedParameters(obj.Id));

Using ForEach in this way is actually nominally slower than writing out a For loop, but much more readable.

Using Linq to run a method on a collection of objects?

Would:

results.AddRange(objects.Select(obj => ApplyFormula(obj)));

do?

or (simpler)

var results = objects.Select(obj => ApplyFormula(obj)).ToList();


Related Topics



Leave a reply



Submit