How to fix this lambda expression? - c#

I have this function for sorting by multiple user selected columns:
public override void Sort(SortFields sortFields)
{
if (sortFields == null || sortFields.Count() == 0) return;
var res = FilteredItems.OrderBy(o => o[sortFields[0].Name], sortFields[0].Ascending ? comparer : comparerDesc);
for (int x = 1; x < sortFields.Count(); x++)
res = res.ThenBy(o => o[sortFields[x].Name], sortFields[x].Ascending ? comparer : comparerDesc);
FilteredItems = new BindingList<T>(res.ToList());
if (ListChanged != null)
ListChanged(this, new ListChangedEventArgs(ListChangedType.Reset, null));
}
The problem, of course, is that when res.ToList() is called x has been incremented to one greater than the highest index in the list and it throws an error. I know I could go ahead and do a ToList() after each sort but that defeats the purpose and isn't even guaranteed to sort correctly with all linq providers. I'm sure there's a simple solution to this... anyone want to tell me what it is? :)

It looks like you may be getting tripped up by a closure over the variable x.
That single variable is incremented on every iteration of the loop. It's incremented one last time, at which point it's 1 greater than the number of items in "sortFields", your conditional statement evaluates to False, and your for loop ends.
As user2864740 pointed out, and Eric states in his article:
Closures close over variables, not over values.
So when you then call ToList(), your chained ThenBy statements retain the final value of the variable x, which was 1 greater than the highest index.
Instead, assign the incrementer to an intermediary variable inside the loop. When you call ToList(), the final value of x won't matter.
for (int x = 1; x < sortFields.Count(); x++)
{
int y = x;
res = res.ThenBy(o => o[sortFields[y].Name], sortFields[y].Ascending ? comparer : comparerDesc);
}
Also from Eric's article, this is (hopefully) being corrected soon, though only in foreach loops apparently, not for loops:
In C# 5, the loop variable of a foreach will be logically inside the loop, and therefore closures will close over a fresh copy of the variable each time. The "for" loop will not be changed.

Try
var res = FilteredItems.OrderBy(o => o[sortFields[0].Name], sortFields[0].Ascending ? comparer : comparerDesc).ToList();

Related

Why isn't assign on a foreach iteration variable in Array.ForEach an error?

Given an array of int numbers like:
int[] arr = new int[] { 0, 1, 2, 3, 4, 5 };
If we want to increment every number by 1, the best choice would be:
for(int i = 0; i < arr.Length; i++)
{
arr[i]++;
}
If we try to do it using foreach
foreach(int n in arr)
{
n++;
}
as expected, we meet the error:
Cannot assign to 'n' because it is a 'foreach iteration variable'
Why if we use this approach:
Array.ForEach(arr, (n) => {
n++;
});
which is equal to the foreach above, visual studio and compiler aren't going to tell us anything, the code is going to compile and just not producing any result in runtime, neither throw an exception?
foreach(int n in arr)
{
n++;
}
This is a language construct, the compiler knows exactly what a foreach-loop is supposed to do and what nis. It can therefore prevent you from changing the iteration variable n.
Array.ForEach(arr, (n) => {
n++;
});
This is a regular function call passing in a lambda. It is perfectly valid to modify local variables in a function (or lambda), so changing n is okay. While the compiler could warn you that the increment has no effect as it's never been used afterwards, it's valid code, and just because the function is called ForEach and actually does something similar to the foreach-loop doesn't change the fact that this is a regular function and a regular lambda.
As pointed out by #tkausl, n with ForEach is a local variable. Therefore:
static void Main()
{
int[] arr = new int[] { 0, 1, 2, 3, 4, 5 };
Console.WriteLine(string.Join(" ",arr));
Array.ForEach(arr, (n) => {
n++;
});
Console.WriteLine(string.Join(" ",arr));
}
will output:
0 1 2 3 4 5
0 1 2 3 4 5
Meaning you don't change the values of arr.
Array.ForEach is not identical to a foreach-loop. It´s an extension-method which will iterate a collection and performs an action on every of its elements.
Array.ForEach(arr, (n) => {
n++;
});
however won´t modify the actuzal collection, it will just re-assign a new value to n which has no relation to the underlying value in the array, because it´s a value-type which is **copied* to the anonymous method. So whatever you do with the param in your anonymous method isn´t reflected to the ForEach-method and thus has no effect in your array. This is why you can do this.
But even if you had an array of reference-types that would work, because you simply re-assign a new instance to the provided parameter, which again has no effect to the underlying array.
Take a look at this simplified example:
MyClass
{
void ForEach(Action<Item> a)
{
foreach(var e in myList)
Action(e);
}
}
In your case the action looks like this:
x => x++
which simply assigns a new value to x. As x however is passed by value, this won´t have any effect to the calling method and thus to myList.
Both are two different things.
First we need to be clear what we need. If the requirement is to mutate the existing values then you can use for loop as modifying the values while enumerating the collection shouldn't be done that' why you face error for the first foreach loop.
So one approach could be if mutating is the intention:
for(int i=0; i< arr.Length; i++)
{
arr[i] = arr[i] +1;
}
Secondly, If the intention is to get a new collection with the updated values then consider using linq Select method which will return a new collection of int.
var incrementedArray = arr.Select( x=> (x+1));
EDIT:
the key difference is in the first example we are modifying the values of colelction while enumerating it while in lambda syntax foreach a delegate is used which get input as local variable.
The foreach statement executes a statement or a block of statements for each element in an instance of the type that implements the System.Collections.IEnumerable or System.Collections.Generic.IEnumerable<T> interface. You cannot modify iterated value because you are using System.Collections.IEnumberable or System.COllections.Generic.IEnumberable<T> interfaces which support deferred execution.
If you want to modify value you can also use
foreach(ref int n in arr)
{
n++;
}
Updated
The Array.Foreach is a method that performs specified action on each element of the specified array. This function support immediate execution behavior and can be applied to only data that holds in memory. The Array.Foreach method take an array and used For loop to iterate through collection.
foreach and Array.Foreach both looks same but are different in their working.

How to get next value when a common variable is increment in a parallel foreach?

I have a common variable that is updated inside a foreach, it is a counter, so I would like to get the next counter to be assigned inside a parallel foreach.
I have something like that:
int myCommonCounter = 5;
Parallel.Foreach(myList, (iterator, state) =>
{
if(iterator.MyProperty == X)
{
iterator.Position = miCommonCounter;
miCommonCounter = miCommonCunter + 1;
}
});
I want to avoid gaps in property iterator.Position and avoid duplicates too.
I have seen an example that does the sum of partial results, for example in this documentation, but it is not really the same case that I need.
So I would like to know if there are any away to update a counter to avoid gaps and duplicates when I updated inside the parallel foreach.
Thanks.
The overload of Parallel foreach that has 3 parameters for lambda argument has counter already.
You should use that instead of writing manual counter. It does not have duplicates. It wont have gaps either unless you stop loop for some reason.
Parallel.ForEach(source.Where(x => /*condition*/), (x, state, counter) =>
{
iterator.Position = (int) counter;
});
Note that counter is type of long so you have to cast it to int if Position is of int type

Is using a math class method in a loop condition slow?

if i have a for loop for example and and i want to use something like Math.round() to round the number but i only need to round the number one time(i.e the number doesn't change throughout the loop) is it better to store it into a variable and then use the variable to in the condition or does it not matter?
for (int i = 0;i < Math.Round(x);i++)
{
//Some code
}
vs
for (int i = 0,roundedX = Math.Round(X); i<roundedX;i++)
{
//Some code
}
The compiler will evaluate the termination condition on every loop iteration.
The compiler and the JITter might hoist some expression or part of an expression outside the loop if it deems it is invariant, meaning it doesn't change. This, however, is usually only done with simpler expressions.
If the Math.Round(X) expression could've been done without an actual method call then perhaps but in this particular case the rounding will happen on each loop iteration.
As such, if you're at last line defense for performance problems, you might consider moving this out and into a variable:
int goal = (int)Math.Round(X);
for (int i = 0; i < goal; i++)
...
As this will call the Math.Round method only once, you only get the performance hit once.
Note that if X changes as part of the loop, then obviously you want to keep your original code.
I would suggest defining a variable
var roundedX = Math.Round(X);
for (int i = 0; i < roundedX; i++)
{
//Some code
}

Replacing CompareTo with LINQ for array elements

I am working on a project which uses posts to represent a fence. Each fence has exactly two posts that implement IComparable and are ordered in each fence. In order to override my CompareTo on Fence, I need to compare post 0 between this and the other fence; if that result returns 0, then I need to compare post 1 between this and the other fence. I wrote a simple for loop to perform this logic, which I've included below. However, Resharper is giving me a warning that I should replace the for loop with LINQ. Is there an easy way to replace the for loop with LINQ?
public int CompareTo(Fence other)
{
for(int i = 0; i < Posts.Length; i++)
{
int c = Posts[i].CompareTo(other.Posts[i]);
if (c != 0)
return c;
}
return 0;
}
Since a Fence has exactly two Posts, then this can be reduced to :
public int CompareTo(Fence other)
{
int c = Post[0].CompareTo(other.Post[0]);
if (c == 0)
c = Post[1].CompareTo(other.Post[1]);
return c;
}
Note that you can (and probably should) replace the Post array with Post0 and `Post1'.
Note, that this could give you a completely different ordering than:
int c = Post[1].CompareTo(other.Post[1]);
if (c == 0)
c = Post[0].CompareTo(other.Post[0]);
which, presumably, is just as valid. (i.e, if this Post[0] is less than the other's, but it's Post[1] is greater, is the Fence greater or less than the other?)
If ReSharper suggests it you may easily hit AltEnterEnter and see what happens. I guess something like:
public int CompareTo(Fence other)
{
return Posts.Select((p, i) => p.CompareTo(other.Posts[i]))
.FirstOrDefault(c => c != 0);
}
This projects each Post to it's comparison result to the respective Post of the other Fence (p is the Post loop variable, i is the index). FirstOrDefault looks for the first non-zero comparsion result or returns 0 if all results are 0.
So this does exactly what your loop does (note that LINQ uses deferred execution, so when the first non-zero comparison occures, no further Posts are compared).
Note that this code is error-prone, as juharr commented: you should first null-check other and check if the two Post arrays have the same length.
(I guess that Posts is not null and the arrays don't contain null elements should be ensured by your classes' implementations).

Comparing One Value To A Whole Array? (C#)

Let's say I have a C# variable and array:
int variable_1 = 1;
int[3] array_1 = {1,2,3};
How can I check if the value of variable_1 is equal to any of the values in array_1 without looping through array_1?
Well something has to loop. Any of the following will work:
bool exists = array.Contains(variable_1);
bool exists = Array.IndexOf(array_1, variable_1) != -1;
bool exists = Array.Exists(array_1, x => x == variable_1);
bool exists = Array.FindIndex(array_1, x => x == variable_1) != -1;
bool exists = array_1.Any(x => x == variable_1);
All of the versions using a lambda expression feel like overkill to me, but they're potentially useful if you find yourself in a situation where you don't know the actual value you're searching for - just some condition.
If you know that the array is sorted, you can use:
bool exists = Array.BinarySearch(array_1, variable_1) >= 0;
That will be O(log n) rather than O(n) (which all the others are), but it does require the array to be sorted first.
Personally I'd normally go with the very first form - assuming you're using .NET 3.5 or higher.
If you need to check for several items and the array is large, you may want to create a HashSet<int>:
HashSet<int> hashSet = new HashSet<int>(array_1);
bool exists = hashSet.Contains(variable_1);
in 3.5 and up
array_1.Contains(variable_1);
or 2.0
array_1.IndexOf(variable_1) != -1
Updated: to save on performance
mmm there can be various options,
var hasItem = Array.Exists(array_1, x => x == variable_1);

Categories