I've stumbled upon a List<T> behaviour that I'm not quite sure I can understand.
I have the following example code
List<int> myInts = new List<int>() {1, 2, 3, 4, 5};
myInts.ForEach( x => x += 1);
The list however, remains unchanged after the ForEach statement. Can someone explain to me why?
int is a value type, which means when the lambda function in the ForEach is called, a copy of the int value is passed, rather than the lambda having a reference available to the original value. It is this copy that is being modified, not the original value in the list.
If you wish to modify the list, you either need to loop through the list modifying each entry individually, or return and assign a new list with the updated values:
//loop through and modify:
for (var x = 0; x < myInts.Count; x++)
myInts[x] += 1;
//or use Select to construct a new list:
myInts = myInts.Select(x => x += 1).ToList();
James Thorpe's answer is totally correct, but I'd like to elaborate on what the difference between ForEach and Select on IEnumerable is.
ForEach
This should be used when you want to use a block lambda (meaning more than just a single expression like most lambda expressions). Generally speaking, this is not heavily used (for the reason you've experienced); you're better off just using a foreach block.
Select
This is for projecting the contents of an IEnumerable into another form; meaning it's exactly what you're looking for. It should be used when you want to go through each element and transform the contents into something else.
Related
I have a List<int> myInts and want to multiply all with 10. I want to use linq (not foreach loop).I tryed this but nothing happend:
List<int> myInts = new List<int>() { 1, 2, 3 };
myInts .ForEach(act => act=act*10);
Of what do I have to take care in the .ForEach(...) part? And yes, I want to use ForEach if it is somehow possible.
Probably its simple, but I cant see it, I apoligize. Thank you all!
This creates a new instance of List.
myInts = myInts.Select(p=>p*10).ToList();
Another and simpler solution:
list = list.ConvertAll(i => i * 10);
"Nothing happens" because reassigning to the local variable (act) has no effect in the caller (ForEach) - C# is Call By Value (except for ref/out parameters).
To modify the list in place, simply use a standard for-each over the indices (which I find readable and upfront of the side-effect intent):
var myInts = new List<int>() { 1, 2, 3 };
for (var i = 0; i < myInts.Count; i++) {
myInts[i] = myInts[i] * 10;
}
To perform the operation and create a new list/sequence (which can be re-assigned to the same variable), see IEnumerable.Select which is a map transformation.
From MSDN documentation:
Modifying the underlying collection in the body of the Action<T> delegate
is not supported and causes undefined behavior.
So, you need to project your exisistin List into a new one, or you need to use a for loop if you must modify the List "in place"
Regards
What is happening is that you are getting a value copy of the int to your the lambda, which so you won't be able to change the 'external' int.
How about projecting a new list?
List<int> myInts = new List<int>() { 1, 2, 3 };
myInts = myInts.Select(act => act*10).ToList();
To use a .Select or .ConvertAll are good solutions.
But my intention was to let "ForEach" return an alterd list.
I found out, over msdn documentation, that this isnĀ“t possible because ForEach is a void type and has no returntype.
This kind of action works if I would have objects in my List instead of ints. Then I would be able to use the "void" Method to change the properties of my objects.
Do you mean like this ?
List<int> _tempList = new List<int>();
myInts.ToList().ForEach(x => _tempList.Add(x * 10));
try this:
Enumerable.Range(0, myInts.Count).ToList().ForEach(i => myInts[i] = myInts[i] * 10);
(I've done as much as possible search based on keywords of "removeall where" or "removeall two argument predicate" without much luck so here goes)
The problem is I have a list of objects (of Class Wave) and a relationship function as:
private bool AinB(Wave A, Wave B), returning true if A 'is in' B. Also AinB(x,y) is true guarantees AinB(y,x) is false.
What's the best way to remove all of the objects in the list where the objects 'is in' another object in the list? i.e., after the removal, the list should only contain objects where neither are in the 'is in' relationship with any other object in the list?
ideally this can be done easily as a
listX.RemoveAll( (x,y) => AinB(x,y)) but of course this is not legal in C#, also there's no easy way to specify which to remove, x or y.
I thought about looping through the list with an index
int i = listX.Count - 1;
while (i>=0)
{
int r = listX.RemoveAll(X => AinB(X, listX[i]));
i = i - r - 1;
}
This seems to work, but I am wondering if there's better way with straight linq code to solve the problem.
Thanks.
Unfortunately I can't think of any way to do this that's not at least O(n^2). But the good news is that it's not that hard from a LINQ perspective:
listX.RemoveAll(item => listX.Any(isin => AinB(item, isin)));
Use a normal for loop that inspects the highest element first down to the lowest element in the list. Inspect the element at the current position for any duplicates within the list, if found remove the current element (and possibly decrement your iterator).
Example:
List<string> stuff = new List<string>(); //full of stuff
for(int i = stuff.Count - 1; i > 0; i--)
{
//Edited here for more efficiency.
for (int x = i - 1; x > 0; x--)
{
if (stuff[x] == stuff[i])
{
stuff.RemoveAt(i);
break; //or possibly continue;
}
}
}
This was hand-coded here so it might have a few syntactical errors, feel free to shoot me an edit if you find something's not quite right.
If you're a wizard with LINQ you could also try grouping the objects in the list and then just selecting the first object in each group for your output list..
you can use the LINQ Except call,
List a = new List();
a.Add("a");
a.Add("b");
a.Add("c");
List b = new List();
b.Add("b");
b.Add("c");
b.Add("d");
List c = a.Except(b);
list c will contain only item "a";
you can even make it more clever by giving a compare object,
List c = a.Except(b, new CompareObject());
Sorry, I think I was not clear earlier. I am trying to do as O.R.mapper says below- create a list of arbitrary variables and then get their values later in foreach loop.
Moreover, all variables are of string type so I think can come in one list. Thanks.
Is there a way to store variables in a list or array then then loop through them later.
For example: I have three variables in a class c named x,y and Z.
can I do something like:
public List Max_One = new List {c.x,c.y,c.z}
and then later in the code
foreach (string var in Max_One)
{
if ((var < 0) | (var > 1 ))
{
// some code here
}
}
Is there a particular reason why you want to store the list of variables beforehand? If it is sufficient to reuse such a list whenever you need it, I would opt for creating a property that returns an IEnumerable<string>:
public IEnumerable<string> Max_One {
get {
yield return c.x;
yield return c.y;
yield return c.z;
}
}
The values returned in this enumerable would be retrieved only when the property getter is invoked. Hence, the resulting enumerable would always contain the current values of c.x, c.y and c.z.
You can then iterate over these values with a foreach loop as alluded to by yourself in your question.
This might not be practical if you need to gradually assemble the list of variables; in that case, you might have to work with reflection. If this is really required, please let me know; I can provide an example for that, but it will become more verbose and complex.
Yes, e.g. if they are all strings:
public List<string> Max_One = new List<string> {c.x,c.y,c.z};
This uses the collection initializer syntax.
It doesn't make sense to compare a string to an int, though. This is a valid example:
foreach (string var in Max_One)
{
if (string.IsNullOrEmpty(var))
{
// some code here
}
}
If your properties are numbers (int, for example) you can do this:
List<int> Max_One = new List<int> { c.x, c.y, c.Z };
and use your foreach like this
foreach(int myNum in Max_One) { ... } //you can't name an iterator 'var', it's a reserved word
Replace int in list declaration with the correct numeric type (double, decimal, etc.)
You could try using:
List<object> list = new List<object>
{
c.x,
c.y,
c.z
};
I will answer your question in reverse way
To start with , you cannot name your variable with "var" since it is reserved name. So what you can do for the foreach is
foreach (var x in Max_One)
{
if ((x< 0) || (x> 1 ))
{
// some code here
}
}
if you have .Net 3.0 and later framework, you can use "var" to define x as a member of Max_One list without worrying about the actual type of x. if you have older than the version 3.0 then you need to specify the datatype of x, and in this case your code is valid (still risky though)
The last point (which is the your first point)
public List Max_One = new List {c.x,c.y,c.z}
There are main thing you need to know , that is in order to store in a list , the members must be from the same datatype, so unless a , b , and c are from the same datatype you cannot store them in the same list EXCEPT if you defined the list to store elements of datatype "object".
If you used the "Object" method, you need to cast the elements into the original type such as:
var x = (int) Max_One[0];
You can read more about lists and other alternatives from this website
http://www.dotnetperls.com/collections
P.s. if this is a homework, then you should read more and learn more from video tutorials and books ;)
What is the best way to convert a List to SortedList? Any good way to do it without cycling through it? Any clever way to do it with an OrderBy()?
WRAP UP
Please read all answers and comments.
Do you mean:
you have a List<T> and wish it to be sorted in place?
you have a List<T> and wish to create another 'list' which is itself sorted
you have a List<T> and wish to make a SortedList<T,T> where the key is the same as the value
Assuming input:
var x = new List<int>() { 3, 2, 1 };
1 is trivial
x.Sort();
2 is trivial
// sx is an IOrderedEnumerable<T>, you can call ToList() on it if you want
var sx = x.OrderBy(i => i);
3 is trivial with a copy
var s = new SortedList<int,int>(t.ToDictionary(i => i));
and more efficiently:
var s = new SortedList<int,int>();
foreach (var i in x) { s[i] = [i]; }
I can't see why you would want to do 3 but there you go.
var list = new List<string>();
var sortedList = new SortedList<string, string>(list.ToDictionary(s => s));
Now I have no clue how efficient this is, but it's one line of code :) Also, in this example I just used the string itself as the selector. In a real scenario, you should know ahead of time what you'd like to use as a selector.
Understand that a List<T> is a smart array, and a SortedList<T, U> is a key/value binary tree. Since there's no relationship between their structures, there can't possibly be a more effective way to do it rather than simply taking each element from the list and putting it into the tree.
If you mean "sorted list" instead of "SortedList," then it's trivial to sort your list via either List.Sort() or an appropriate OrderBy().
List unsortedPersons = new List();
// ... Populate unsortedPersons ...
var sorted = from person in unsortedPersons
orderby person.Name
select person;
The LINQ gives you an ISortedEnumerable i believe, which may be good enough for your purposes.
I want to union, merge in a List that contains both references, so this is my code, how can I define a list ready for this porpouses?
if (e.CommandName == "AddtoSelected")
{
List<DetalleCita> lstAux = new List<DetalleCita>();
foreach (GridViewRow row in this.dgvEstudios.Rows)
{
var GridData = GetValues(row);
var GridData2 = GetValues(row);
IList AftList2 = GridData2.Values.Where(r => r != null).ToList();
AftList2.Cast<DetalleCita>();
chkEstudio = dgvEstudios.Rows[index].FindControl("ChkAsignar") as CheckBox;
if (chkEstudio.Checked)
{
IList AftList = GridData.Values.Where(r => r != null).ToList();
lstAux.Add(
new DetalleCita
{
codigoclase = Convert.ToInt32(AftList[0]),
nombreestudio = AftList[1].ToString(),
precioestudio = Convert.ToDouble(AftList[2]),
horacita = dt,
codigoestudio = AftList[4].ToString()
});
}
index++;
//this line to merge
lstAux.ToList().AddRange(AftList2);
}
dgvEstudios.DataSource = lstAux;
dgvEstudios.DataBind();
}
this is inside a rowcommand event.
If you want to add all entries from AftList2 to lstAux you should define AftList2 as IEnumerable<> with elements of type DetalleCita (being IEnumerable<DetalleCita> is enough to be used as parameter of AddRange() on List<DetalleCita>). For example like this:
var AftList2 = GridData2.Values.Where(r => r != null).Cast<DetalleCita>();
And then you can add all its elements to lstAux:
lstAux.AddRange(AftList2);
Clarification:
I think you are misunderstanding what extension method ToList() does. It creates new list from IEnumerable<T> and its result is not connected with original IEnumerable<T> that it is applied to.
That is why you are just do nothing useful trying to do list.ToList().AddRange(...) - you are copying list to (another newly created by ToList()) list, update it and then basically throwing away it (because you are not even doing something like list2 = var1.ToList(), original var1 stays unchanged after that!!! you most likely want to save result of ToList() if you are calling it).
Also you don't usually need to convert one list to another list, ToList() is useful when you need list (List<T>) but have IEnumerable<T> (that is not indexable and you may need fast access by index, or lazy evaluates but you need all results calculated at this time -- both situations may arise while trying to use result of LINQ to objects query for example: IEnumerable<int> ints = from i in anotherInts where i > 20 select i; -- even if anotherInts was List<int> result of query ints cannot be cast to List<int> because it is not list but implementation of IEnumerable<int>. In this case you could use ToList() to get list anyway: List<int> ints = (from i in anotherInts where i > 20 select i).ToList();).
UPDATE:
If you really mean union semantics (e.g. for { 1, 2 } and { 1, 3 } union would be something like { 1, 2, 3 }, with no duplication of equal elements from two collections) consider switching to HashSet<T> (it most likely available in your situation 'cause you are using C# 3.0 and I suppose yoou have recent .NET framework) or use Union() extension method instead of AddRange (I don't think this is better than first solution and be careful because it works more like ToList() -- a.Union(b) return new collection and does NOT updates either a or b).