Iterate over list of iterators linq - c#

List enumerators not working in case when they are put in a list. For example in case of two lists (two enumerators)
public void test()
{
var firstList = new List<int>() { 1, 2, 3 };
var secondList = new List<int>() { 4, 5, 6 };
var lists = new List<List<int>>();
lists.Add(firstList);
lists.Add(secondList);
// Not working
var iterators = lists.Select(x => x.GetEnumerator()).ToList();
iterators.ForEach(x => x.MoveNext());
iterators.ForEach(x => Console.WriteLine(x.Current));
// Working
var firstIterator = iterators[0];
var secondIterator = iterators[1];
firstIterator.MoveNext(); secondIterator.MoveNext();
Console.WriteLine(firstIterator.Current);
Console.WriteLine(secondIterator.Current);
}
the first part is not working and it prints 0 0, while the second part is working and it prints 1 4.
I don't understand what is mistake with the first part and how it can be solved.

This is because the List<T>.GetEnumerator method returns a List<T>.Enumerator mutable struct.
When you assign it to a variable and call MoveNext and Current, it works. But when passed to delegate, it is passed by value, hence the delegate receives a copy so calling MoveNext has no effect of the original struct Current.
Just one of the side effects of the mutable structs.
If you change
x => x.GetEnumerator()
to
x => (IEnumerator<int>)x.GetEnumerator()
or
x => x.AsEnumerable().GetEnumerator()
both snippets will work because now the iterators will contain a boxed references of the returned structs.

Its because you run 2 separate .ForEach() in the not working part. Do both actions in 1 foreach (MoveNext(), then print). MoveNext will not be remembered in a .Foreach() for a good explaination: Go to this answer
This:
iterators.ForEach(x => x.MoveNext());
iterators.ForEach(x => Console.WriteLine(x.Current));
Becomes:
iterators.ForEach(x => { x.MoveNext(); Console.WriteLine(x.Current); });

List's enumerator is implemented as a value type (source code):
iterators[0].GetType().IsValueType // true
That means - iterators are passed by value when you are passing them to the methods (i.e. copy of iterator is passed instead of passing a reference to iterator instance). ForEach method simply executes an action in a loop:
iterators.ForEach(copy => copy.MoveNext());
Action delegate is a method, so you are passing a copy of each iterator to this method. So original iterators remain unchanged. And when you call ForEach second time, again, copies of original untouched iterators are passed there:
iterators.ForEach(newCopy => Console.WriteLine(newCopy.Current));
But original iterators are in the initial state (before the first item) that is why you see default values of integer type when you read Current value.
You can either work with single copy of iterator:
iterators.ForEach(copy => { copy.MoveNext(); Console.WriteLine(copy.Current); });
Or box value type to pass it by reference (note that type of iterators list will change):
var iterators = lists.Select(x => (IEnumerable<int>)x.GetEnumerator()).ToList();

Related

Removerange method after the use of take method from the list<T> has unexpected behavior

Does anyone knows why the length after leaving the while loop body in the below code is zero? Please do not provide me chunking algorithm I am already aware of several such algorithms and I know how to solve this problem. My question is only about the wired behavior of RemoverRange or perhaps Take.
static void Main(string[] args)
{
var list = new List<int>();
for (int i = 0; i < 1000; i++)
{
list.Add(i);
}
var chunks = new List<IEnumerable<int>>();
while (list.Count > 0)
{
chunks.Add(list.Take(10));
list.RemoveRange(0, 10);
}
int length = chunks.ToList()[0].Count();
}
At the following line:
var chunks = new List<IEnumerable<int>>();
you create a list, whose Count is 0, since there aren't any items in the list. Then at the foreach statement at each step you add list.Take(10) and after this you Remove the first 10 items from list. The important this here is to realize that list.Take(10) is lazy evaluated (more often you will hear the term deferred execution). The time that this would first be evaluated is at the following line:
int length = chunks.ToList()[0].Count();
At this line list.Take(10) would be evaluated and since you have removed all the items form the list, there aren't any elements in list. For this reason, list.Take(10) return an empty sequence and consequently the chunks.ToList()[0] would be an empty list.
Update deferred execution explanation:
Let that we have the following list:
var list = new List<int> {1, 2, 3, 4, 5 };
var firstThree = list.Take(3);
The variable firstThree holds a reference to an enumerator - specifically an IEnumerable<int>. It does not hold an array or a list of 1,2,3. The first time you will use this iterator you will start to "fetch" data from the list.
For instance you could call the ToList or ToArray methods:
var firstThreeList = firstThree.ToList();
var firstThreeArray = firstThree.ToArray();
Both the above calls would put the iterator in action - in general terms would force the execution of your query in LINQ -. At this very moment, you will traverse the list and you will fetch the first three items.
That being said, it is clear that if in the meanwhile you have modified the list by removing all the numbers from iist, there wouldn't be any elements in the list and you will get nothing.
As a test I would suggest you run the above code once and then run it again but before ToList and ToArray calls to make this call:
list.RemoveAll(x => true);
You will notice now that both firstThreeArray and firstThreeList are empty.
The line
chunks.Add(list.Take(10));
does not actually take 10 items from list, it only tells to take 10 when chunks[i] is first referenced (through e.g. Count()). Since you are altering the list, the reference points to an empty list. You can force the evaluation of the list using
chunks.Add(list.Take(10).ToList());
The problem you're experiencing is that LINQ is kind of like a view. It only actually iterates through the collection when you call a method that requires it to produce a specific value (First(), Last(), Count(), etc.). So the list is only evaluated at the point where you call one of these methods.
chunks.Add(list.Take(10));
This code effectively says "take a reference to list, and when somebody iterates you, only go as far as the first 10 items". To resolve this, you can convert that small section to a list (evaluate those 10 items, and create a new list from them):
chunks.Add(list.Take(10).ToList());
Consider this code:
List<string> names = new List<string>() { "a", "b", "c" };
IEnumerable<string> skip2 = names.Skip(2);
Console.WriteLine(string.Join(", ", skip2)); // "c"
names.Add("d");
names.Add("e");
Console.WriteLine(string.Join(", ", skip2)); // "c, d, e"
Because you use the iterator (IEnumerable<string> skip2) each time you call string.Join(", ", skip2) it will iterate through the list each time, even if the list has changed.
As such, you will get "c" on the first run, and "c, d, e" on the second run.
In fact, this would be perfectly valid (although harder to read):
List<int> list = new List<int>();
IEnumerable<int> values = list.DefaultIfEmpty(0);
list.Add(5);
list.Add(10);
list.Add(15);
Console.WriteLine(values.Average()); // 10
There is no weird behaviour at all. That is exactly the expected behaviour for IEnumerable. What you should keep in mind is that IEnumerable is lazily evaluated, which means it is evaluated when is enumerated, i.e when you actually access the said IEnumerable. What you are doing is basically→
①Get the reference to list object
②Prepare to take the first 10 elements of the said list object, do not yet evaluate!
③Add the not yet evaluated object, in this case→(LINQ.Take(10)) into chunks list.
④Remove the first 10 elements of list
⑤Rinse and Repeat until there are no more items in list
⑥Create a list, which is all made up not yet evaluated items of list.Take(10).
⑦You take the first element of the said chunks, which is not yet evaluate but is a reference to the first 10 elements of list, which is empty!!!
⑧You call Count on IEnumerable instance, which finally evaluates the first ten elements of an enmpy list

c# is it possible to lazy load function parameter after calling the function?

I was wondering if it possible in C# to lazy load the parameter of a function after calling the function. In fact I want the parameter of the function to be loaded only when I use the output of the function.
I try to explain what I mean with the following example:
var a = Enumerable.Range(1, 10);
int take = 5;
var lazyTake = new Lazy<int>(() => take);
// here I still don't iterate on Enumerable, I want the parameter of function Take be initialized later when I start iterating
var b = a.Take(lazyTake.Value);
// here I initialize (change) the value of parameter take
take = 6;
Console.WriteLine(b.ToList().Count); // I want b to have 6 elements but it's 5
Here Lazy<int> is not doing what I need. Does anyone know any workaround or language feature to support such a case?
public static IEnumerable<T> Take<T>(this IEnumerable<T> source, Lazy<int> count) {
var takeSequence = source.Take(count.Value);
foreach (var item in takeSequence) yield return item;
}
This is fully lazy. The body of this function will only execute when you start enumerating because this is an iterator method. Only then will the lazy count be forced to materialize.
Instead of a Lazy you could pass a Func<int> getTakeCount parameter as well.
Lazy realizes it's value at the time you access the .Value property. So at the time you call a.Take, you've gotten the actual int value 5. Changing the take variable won't help at this point, the laziness is gone.
You need a function that will take a Lazy<T>, not a T. You can probably write one without too much trouble if you understand how to implement IEnumerable<T>, but there's nothing built into the framework I know of to suit your scenario.
Everything is right, the value is initializing lazily, but the problem is the value is being evaluated when you call a.take(lazyTake.Value) because you're passing it as a parameter to a function and it must be evaluated.
The best you can do is surround it with a lambda and execute the lambda at the end:
var a = Enumerable.Range(1, 10);
int take = 5;
// here I still don't iterate on Enumerable, I want the parameter of function Take be initialized later when I start iterating
Func<IEnumerable<int>> getResult = () => a.Take(take);
// here I initialize (change) the value of parameter take
take = 6;
Console.WriteLine(getResult().ToList().Count);
EDIT: cannot use var for the lambda, just use Func to make it work

Why can the anonymous type be changed if it is supposed to be immutable?

I thought I had a good understanding of the anonymous type, but this small code snippet has left me a little confused:
string[] arr = { "Agnes", "Allan", "Benny" };
var result = arr.Where(a => a.StartsWith("A")).Select(a => a);
// Why can I do the below, if arr is immutable?
result = arr.Where(a => a.EndsWith("n")).Select(a => a);
What I don't understand is why I am allowed to assign a second value to result. I mean isn't the idea of anonymous types being immutable, that they cannot be changed after they have got their initial value?
First, there is no anonymous type involved.
This string[] arr = { "Agnes", "Allan", "Benny" }; is an array creation expression.
result is IEnumerable<string> and in both LINQ statements you are just creating a query.
This is what is going on:
array creation expression
string[] arr = { "Agnes", "Allan", "Benny" };
query arr and returns IEnumerable<string>
var result = arr.Where(a => a.StartsWith("A")).Select(a => a);
assigns results a new query on arr returning IEnumerable<string>
result = arr.Where(a => a.EndsWith("n")).Select(a => a);
As far as, for understanding immutability, think of String also see this article: Immutable types: understand their benefits and use them
You have an anonymous type when you do something like:
var anon = new { X = 5, Y = 6 };
There are some pretty simple rules: you can't express the type of an anonymous type (so often you use var)... there must be a new {... You must give a name to the properties and a value X = 5.
What you are doing is creating an array of string[] using an array initializer. You are even writing it:
string[] arr = ...
And you aren't modifying anything... result is another variable, referencing an IEnumerable<> (a new object you are creating) and then referencing another IEnumerable<> At the end of your code you have 6 objects (a little more, but we will ignore some invisible objects):
The array referenced by arr (and referenced by the two IEnumerable<>)
The second IEnumerable<>, referenced by result, that has a reference to arr
The first IEnumerable<>, not referenced by anyone (the GC will collect it before or later), that has a reference to arr
3x string, all referenced by arr. Note that IEnumerable<> are "lazy", so they don't contain any reference to any string
The result variable is assigned twice, to two different IEnumerable<>. It is nearly always legal to reassign variables (exception are readonly fields). it is clearly legal to do:
string foo = "Foo";
foo = "Bar";
Another useful concept to understand is the difference between type, instance and variable.
Simplifying, type is like a blueprint, it describes what an instance of the type will look like:
class Car
{
public int Doors {get; set;}
public string EngineType { get; set;}
}
The code above describes type. You can make many instances of this type:
Car truck = new Car { Doors = 2, EngineType = "BigEckingEngine" };
Car pickup = new Car { Doors = 5, Engine Type = "4 cylinder" };
etc...
Note how variables truck and pickup house your instances. But variables are just that, they can house any instance of their respective type, so while it does not make much sense you can do this:
Car tmp = truck;
truck = pickup;
pickup = tmp;
The instances themselves has not changed. But the variables now hold different instances.
The instances of this example Car class above are mutable. So you can do this:
pickup.Doors = 99;
Should the type be immutable, you would not be able to do that, but you are still can do variable assigning as in the example with the tmp variable freely, regardless of type being mutable or not, because such assignment do not change instances.
As noted, your example does not contain an anonymous type, but even if it did, it does not involve any kind of mutation you are asking about.
LINQ methods like Where() and Select() don't change the underlying array. It creates a new object. The created result is of type IEnumerable<string>, LINQ just filters the array so if you will iterate over it later, you will just get values that match Where and Select but your arr object will remain unchanged.
It's worth expanding the other answers to show that a CONCRETE resolution of a LINQ query is not the same as a IEnumerable<T> and that neither have anything to do with anonymous type immutability.
If you created the following array of anonymous types:
var arr = new[] { new { Name = "Agnes"}, new { Name = "Allan" }, new { Name = "Benny" }};
arr.GetType().Dump();
var result = arr.Where(a => a.Name.StartsWith("A")).Select(a => a)
result = arr.Where(a => a.Name.EndsWith("n")).Select(a => a);
result.Dump();
in my case,
<>f__AnonymousType0`1[System.String][]
and
"Allan"
are respectively outputted, because result type is actually
System.Linq.Enumerable+WhereSelectArrayIterator`2[
<>f__AnonymousType0`1[System.String],
<>f__AnonymousType0`1[System.String]]
In addition, if I try to resolve the IEnumerable and then re-update the result:
var result = arr.Where(a => a.Name.StartsWith("A")).Select(a => a).ToList();
result = arr.Where(a => a.Name.EndsWith("n")).Select(a => a).ToList();
I once again get an output of
"Allan"
However, in this case my result type has been reevaluated to
System.Collections.Generic.List`1[<>f__AnonymousType0`1[System.String]]
since ToList() is creating a new collection. I can technically add and remove to that collection at will since the collection itself is quite willing to be mutated.
Finally, that does not mean that the underlying anonymous type object is not immutable!
result.First ().Name = "fail";
Will fail, regardless of result being a list with the following error:
Property or indexer 'AnonymousType#1.Name' cannot be assigned to -- it
is read only
precisely because it is immutable.

Find all elements that are lesser than or equal to the value

I'm new in c# and I have an array like this:
int[] cost = new int[] {10, 15, 20, 30, 50};
What is the simplest code to find/get all values that are lesser than or equal to the value?
The given value is 29, would return 20, 15, and 10.
Here's one of the simplest things you can do for this:
var resultArray = cost.Where(item => item <= 29).ToArray();
Here's another:
var resultArray = Array.FindAll(cost, item => item <= 29);
The first option uses an extension methods named Where that operates on IEnumerable, which arrays implement. IEnumerable is an interface for a sequence of elements that can be stepped through one after the other.
The second option uses FindAll, which is actually built in to the Array class.
The item => item <= 29 stuff is lambda notation. It's a highly concise way of defining a function on the spot.
This way of writing an anonymous function on the spot is saying
Where or FindAll each receive an argument that is an anonymous function, and we will define that argument that is an anonymous function right here in the actual call to Where() or FindAll().
The anonymous function itself receives as its own one argument an element, which we'll call item (the name could be anything, it could also be x). This item is an individual element of the Array (or IEnumerable)--elements being ints in this case--which the compiler infers. The name of the function argument is left of the =>
the function body is right of the =>. (Read => as "this is a function where the argument item goes to the body right of the => ") The body is an expression that evaulates to a Boolean true or a false, so the return value of the function is a Boolean.
So, essentially item => item <= 29 is the same as declaring a function like the below, but it's much more concise and can be easily used as an argument to another function. Here's the long way of declaring such a function, so you can see that the lambda way of writing it is much more concise:
Boolean int func(int x)
{
if (x<=29) {
return true;
} else {
return false;
}
}
the Where or Array.FindAll functions call the lambda function on each element in the Array or IEnumerable and return (yield) back only those items where the lambda function returns true.
Update
Looks like you edited the original question to remove a reference to finding indexes, so the below is no longer relevant (the above stuff finds values of elements, not indexes of elements). But the below shows how to find both values and indexes of the array elements that satisfy the condition:
You mentioned indexes. This version also finds the indexes of the original array (not just the elements) that satisfy the criterion:
List<int> indexesInvolved = new List<int>();
var resultArray = cost.Where((item, index) =>
{
if (item <= 29) {
indexesInvolved.Add(index);
return true;
}
else {
return false;
}
}
).ToArray();
var foundIndexArray = indexesInvolved.ToArray();

Union two List in C#

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).

Categories