List's ForEach() method not acting as expected - c#

Why is this not working?
List<int> test = new List<int>();
for (int i = 0; i < 20; ++i)
test.Add(i);
test.ForEach(t => t = ++t);
The elements in test are not incremented.

Firstly, List<T>.ForEach isn't part of LINQ. It's been in .NET since 2.0.
Elements in test are not incremented
No, they wouldn't be. The parameter to Action<T> is passed by value, not by reference. Changes made to the parameter (t in your case) aren't visible to the calling code - and even if they were, there's no guarantee that List<T>.ForEach would use them.
Note that if you had a reference type (e.g. StringBuilder) as the list element type, and made changes to the data in the object that the parameter value referred to, e.g.
builders.ForEach(x => x.Append("Foo"))
then those changes would be visible in the list, because they're not changes to the list at all - the list would contain the same references as before, just to objects whose data happened to have changed.

Ints are values, not references.
Plus a foreach doesn't allow manipulation of the collection elements.
So it's a double fail i'm afraid :(
What would work:
for(int i=0;i<test.Count;i++)
test[i]++;

The t variable that you have is a copy of the item in the list. You are modifying a copy, and as such the item in the list itself isn't affected. To increment each item in the list you can use a for loop:
for(int i = 0; i < test.Count;i++
test[i]++;

This is the expected result.
You wouldn't expect the following to actually increment x in Main, so your lambda example is no different.
static void Main(string[] args)
{
int x = 1;
Increment(x);
Console.WriteLine("{0}");
}
static void Increment(int x)
{
x = ++x;
}

List<int> test = new List<int>();
for (int i = 0; i < 20; ++i)
test.Add(i);
for(var z = 0 ; z < test.Count; z++)
{
test[z]++;
}

Related

How to change the value of a primitive data type variable referred to by a foreach iterator

If I have a class Ball with a set accessor for its property radius, I can set the radius of a list of Ball objects using a foreach loop:
foreach(Ball ball in balls)
{
ball.radius = 1;
}
But if I am iterating through a collection of primitive data types, this does not work because the primitive does not have accessors. If I try to change the value of the primitive data type, I am actually performing the operation on the iterator, not to value itself, so the following does not work:
foreach(double radius in radii)
{
radius = 1;
}
I tried solving it with a pointer, like this:
double* p;
foreach(double radius in radii)
{
p = &radius;
*p = 1;
}
Unsurprisingly, this does not work because p refers to the address occupied by the iterator, not the variable.
In case it is important here, I will be more specific about what I am actually trying to do, and although I would still like an answer to my main question, it's quite possible that there is a much simpler way to do this with the methods of List<>, which I am not too familiar with. I have a List<List<double>>, which I want to alter every element of, specifically by dividing each by a normalisation constant normalisationFactor. The following works perfectly:
for (int i = 0; i < array.Count; i++)
{
for (int j = 0; j < array[0].Count; j++)
{
array[i][j] /= normalisationFactor;
}
}
However, I find the foreach syntax much more readable, and would like to to it that way if I can. For the reasons I have detailed above, the following does not work:
foreach (List<double> row in array)
{
foreach (double value in row)
{
value /= normalisationFactor;
}
}
So, how do I achieve what I want using a foreach loop?
primitives (structs) are immutable and passed around as values not pointers.
IEnumerables do not provide an means to update the data.
If you know the underlining data type (Array, Collection, List), just loop the position index and update the collection position
for (var i=0; i < radius.Count; i++)
radius[i] = newValue

Method for sorting a list in C# [duplicate]

This question already has answers here:
How to Sort a List<T> by a property in the object
(23 answers)
Closed 6 years ago.
I've written the the selection sort method beneath. I would like to retain the code in general as it's a school exercise, but I understand that there are more correct ways to do it, as with Linq.
It works well besides that it only sorts the property PersonalNumber. I can see where the error is the following:
temp = list[i].PersonalNumber;
list[i].PersonalNumber = list[posMin].PersonalNumber;
list[posMin].PersonalNumber = temp;
Is there any way to sort all of the properties contained for each index in the list? Or do I have to write the above code for each property? There are three properties in total.
Full method:
public static void SelectionSort(List<Person> list) {
// With this method the Person list is sorted in ascending order.
//posMin is short for position of min
int posMin, temp;
for (int i = 0; i < list.Count - 1; i++) {
posMin = i;//Set posMin to the current index of array
for (int j = i + 1; j < list.Count; j++) {
if (list[j].PersonalNumber < list[posMin].PersonalNumber) {
//posMin will keep track of the index that min is in, this is needed when a swap happens
posMin = j;
}
}
//if pos_min no longer equals i than a smaller value must have been found, so a swap must occur
if (posMin != i) {
temp = list[i].PersonalNumber;
list[i].PersonalNumber = list[posMin].PersonalNumber;
list[posMin].PersonalNumber = temp;
}
}
}
If you want to sort list in place, just put Sort:
list.Sort((x, y) => x.PersonalNumber.CompareTo(y.PersonalNumber));
To sort in descending order, add -:
list.Sort((x, y) => -x.PersonalNumber.CompareTo(y.PersonalNumber));
It's definitely not something you should do manually (unless you're training your algorithmics skills :) ). It will make your code more complex and harder to maintain.
Just put:
using System.Linq;
and do this:
var sorted = list.OrderByDescending(x => x.PersonalNumber).ToList();
you don't need to be Linq ninja to use it. I also strongly recommend to start using it. I think you can agree it's very easy to read and quite obvious what is it doing.
Ah, and if you're wanting to sort ascending, just use .OrderBy instead of .OrderByDescending.
For most scenarios, you should use one of the built-in functionalities for sorting, such as List<T>.Sort or Enumerable.OrderBy. I'm assuming that you want to retain your own implementation for the sorting algorithm.
You can introduce a key selector function as the second argument to your method:
public static void SelectionSort<TSource, TKey>(
List<TSource> list,
Func<TSource, TKey> keySelector)
{
// With this method the list is sorted in ascending order.
//posMin is short for position of min
int posMin;
for (int i = 0; i < list.Count - 1; i++) {
posMin = i;//Set posMin to the current index of array
for (int j = i + 1; j < list.Count; j++) {
if (keySelector(list[j]) < keySelector(list[posMin])) {
//posMin will keep track of the index that min is in, this is needed when a swap happens
posMin = j;
}
}
//if pos_min no longer equals i than a smaller value must have been found, so a swap must occur
TSource temp;
if (posMin != i) {
temp = list[i];
list[i] = list[posMin];
list[posMin] = temp;
}
}
}
You would then consume this with a lambda expression:
SelectionSort(persons, (Person p) => p.PersonalNumber);

C# Using .Count with Tuple Items

I am trying to compare the contents of a number of Tuples inside of a List. The code runs reasonable well, but on certain occasions, completeList[z].Item1.Count will come back with the wrong number. When I look in the debugger, in one example a = completeList[z].Item1.Count might return 18 when the count is 9. So this causes it to go out of range.
completeList.Count returns correctly, as does completeList[z].Item1[b], so I'm not sure what I could be doing wrong with completeList[z].Item1.Count.
Thanks for any insights!
int x = completeList.Count;
int y = 0;
while (y < x)
{
for (int z = 0; z < x; z++)
{
if (y != z && completeList[y].Item6.Equals(completeList[z].Item6))
{
int a = completeList[z].Item1.Count;
a = a -1;
for (int b = 0; b < a; b++)
{
if (completeList[y].Item1.Contains(completeList[z].Item1[b]))
{
completeList[z].Item1.RemoveAt(b);
completeList[z].Item2.RemoveAt(b);
completeList[z].Item3.RemoveAt(b);
completeList[z].Item4.RemoveAt(b);
completeList[z].Item5.RemoveAt(b);
}
}
}
}
y++;
}
You're removing items from the list. This is changing the count, and also moving around the indexes of the existing items. The count is correct at the start of the loop, but by the time you've actually removed an item, the count is no longer valid.
The most effective solution is to not iterate the list while removing items at all; instead rely on the list's RemoveAll method to remove all of the items that match your list of items to remove. While it's possible for you to manage the indexes properly to remove while iterating, you should avoid this complexity.

Best way to set IEnumerator to end

I have custom class that implement IEnumerable<int[]> interface and save current Enumerator. When added new element I need set enumerator to last element.
Sample code for set enumerator to end:
IEnumerable<int> seq = ...
seq.Reset();
for (int i = 0; i < seq.Count; i++)
{
seq.MoveNext();
}
How do this faster?
(Can I go to the end can't not scroll all sequence elements?)
if faster means less code (another alternative is),
IEnumerable<int> seq = ...
while (seq.MoveNext())
{
var item = seq.Current;
}
EDIT
you want seq.Last()
It is an extension method and has code similar to above.
EDIT2
i need seq.Current=seq.Last();
your code is similar to
IEnumerable<int> seq = ...
int count=0;
//Following while is equivalent to seq.Count
while (seq.MoveNext())
{
count++;
}
int i=0;
while (i<count)
{
seq.MoveNext();
}
Using IEnumerator only it is not possible to set seq.Current to Last in one iteration as you never know where to stop.
The for statement will iterate from x to y, with a pre-defined increment, like so:
for (`start`; `until`; `increment`)
e.g. If we wanted to loop from 0 to 9, with an increment of 1, we would write
for (int i = 0; i < 10; i++)
If you have an object that implements a next type method, you may want to use a while loop.
while (seq.CurrentItem != null)
{
// do something with the current item and move to next
seq.Next();
}
I would recommend reading up about loops in C#.

Enumerable.Repeat has some memory issues?

I initialized an Array as
Double[][] myarr = Enumerable.Repeat(new double[12], 13).ToArray();
Then in a loop i am incrementing values like
myarr[0][0]++;
This causes all values like myarr[1][0], myarr[2][0], myarr[3][0] ..... myarr[12][0] to increment by one.
This problem is not occurring when using a for loop (0-12) i am initializing like
myarr[i] = new double[12];
Why is this so?
Other answers have explained the problem. The solution is to create a new array on each iteration, e.g.
double[][] myarr = Enumerable.Range(0, 13)
.Select(ignored => new double[12])
.ToArray();
It's because new double[12] creates a single array object in memory - Enumerable.Repeat is simply providing you with multiple references to that array.
This is expected behavior - array is a referebce type. You are creating a jagged array i.e. array of arrays. All elements of your outer array references the same inside array i.e the first argument of Repeat call, so changes in the inside array will be reflected at on indices (because all indices refer to the same array).
With new double[12] you are creating reference to array of doubles, and then you repeate the reference 12 times, so myarr[0..n] will have reference to one memory region.
You can use the folowing method to resolve thw issue
static T[][] CreateArray<T>(int rows, int cols)
{
T[][] array = new T[rows][];
for (int i = 0; i < array.GetLength(0); i++)
array[i] = new T[cols];
return array;
}
Or with custom Repeat method which calls action every step:
public static IEnumerable<TResult> RepeatAction<TResult>(Action<TResult> elementAction, int count)
{
for (int i = 0; i < count; i++)
{
yield return elementAction();
}
yield break;
}
usage
RepeatAction(()=>new double[12], 12);
Arrays are references. In the Repeat call you create one array and assign its reference 12 times. In your loop however you create 12 distinct arrays.
Repeat() basically just capture and yields the same element multiple times, so you got multiple references to the same object instance in the memory.
This is how Repeat() is implemented:
private static IEnumerable<TResult> RepeatIterator<TResult>(TResult element, int count)
{
for (int i = 0; i < count; i++)
{
yield return element;
}
yield break;
}

Categories