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 MyObject
s 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
Read from Word Document Line by Line
How to Mix Colors "Naturally" with C#
Stream.Seek(0, Seekorigin.Begin) or Position = 0
Dependency Injection VS Service Location
.Net Core 3.0: Razor Views Don't Automatically Recompile on Change
Why Use Asqueryable() Instead of List()
Datetimepicker: Pick Both Date and Time
Pair Bluetooth Devices to a Computer with 32Feet .Net Bluetooth Library
Put Content in Httpresponsemessage Object
Display Hourglass When Application Is Busy
How to Create a Friendly Url in ASP.NET MVC
Passing Just a Type as a Parameter in C#
Large Wcf Web Service Request Failing with (400) Http Bad Request